OPEN API and C# - does it play together?

Document created by Lukasz Czerpak Employee on Dec 14, 2016Last modified by Lukasz Czerpak Employee on Dec 11, 2017
Version 4Show Document
  • View in full screen mode

Recently I've been asked several times to help developing API client in C#, therefore decided to create step-by-step guide on how to use EdgeGrid library for C# language and implement simple purge functionality using CCU API. It illustrates usage of v2 as well as v3 (Fast Purge) of the API. It covers purge by URL and purge by CpCode.

Other APIs may require to use different structures, but whole concept remains the same and example below can be foundation for more sophisticated functionality. Let's get started then!

 

First create a project in Visual Studio, for our example it will be Windows / Console Application:

 

Once the project is created, we can add simple code to output text to console, build and run it to verify that at this point everything works fine and our environment is properly set up. The code is as follows:

using System;

namespace purge_api_sample
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Aloha!");
            Console.ReadLine();
        }
    }
}

 

Once you run it, console will appear with "Aloha!" message printed out and it will be waiting for <ENTER>:

We will need two external libraries:

  • EdgeGrid - library which assists in the interaction with {OPEN} API
  • Newtonsoft.Json - popular high-performance JSON framework we will use for purge objects serialization

 

Both can be installed from Visual Studio - click right button on your project and select "Manage NuGet Packages...":

On the next screen switch to "Browse" tab and find Newtonsoft.Json package and install it:

 

Similarly with Akamai's EdgeGrid:

 

Last pre-requisite to start hacking Purge API is API credentials. The process is common for all {OPEN} APIs and is very well documented here: Authorize Your Client . You can skip it if you already have your API credentials and read/write access on Content Control Utility v2/v3.

 

Note: The example we're building right now supports CCU v2 and CCU v3, just to illustrate differences in code. But it's up to you which one API you'd like to use.

 

Let's create Purge Object classes that will be used later on in the code:

 

public class PurgeObject
{
}

public class PurgeObjectV2 : PurgeObject
{
    [JsonProperty("objects")]
    public string[] Objects { get; set; }
    [JsonProperty("type")]
    public string Type { get; set; }
    [JsonProperty("action")]
    public string Action { get; set; }
    [JsonProperty("domain")]
    public string Domain { get; set; }
}

public class PurgeObjectV3 : PurgeObject
{
    [JsonProperty("objects")]
    public string[] Objects { get; set; }
    [JsonProperty("hostname")]
    public string Hostname { get; set; }
}

 

Note: Please notice that JsonProperty annotations are needed to properly serialize attributes - specifically in this example properties are camel case but {OPEN} API expects lower case in JSON. 

 

The following code is a procedure which takes single PurgeObject as an argument and executes purge action on either of APIs:

static void purge(PurgeObject purgeObject)
{
    const String client_token = "akab-*********************************";
    const String access_token = "akab-*********************************";
    const String secret = "*********************************";
    const String api_url = "https://akab-*********************************.purge.akamaiapis.net";

    ClientCredential credentials = new ClientCredential(client_token, access_token, secret);
    EdgeGridV1Signer signer = new EdgeGridV1Signer(null, 100000);

    Uri uri = new Uri(api_url + "/ccu/v3/invalidate/url/production");
    if (purgeObject is PurgeObjectV2)
        uri = new Uri(api_url + "/ccu/v2/queues/default");

    WebRequest request = WebRequest.Create(uri);
    request.Method = "POST";
    request.ContentType = "application/json";
    ServicePointManager.Expect100Continue = false;

    // convert string to stream
    byte[] byteArray = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(purgeObject));
    MemoryStream uploadStream = new MemoryStream(byteArray);

    // Signs the current request using the Akamai C# library. Creates an Authentication header for the request.
    request = signer.Sign(request, credentials, uploadStream);

    if (uploadStream == null)
        request.ContentLength = 0;
    else if (uploadStream.CanSeek)
        request.ContentLength = uploadStream.Length;
    else if (request is HttpWebRequest)
        ((HttpWebRequest)request).SendChunked = true;

    if (uploadStream != null)
    {
        // avoid internal memory allocation before buffering the output
        if (request is HttpWebRequest)
            ((HttpWebRequest)request).AllowWriteStreamBuffering = false;

        using (Stream requestStream = request.GetRequestStream())
        using (uploadStream)
            uploadStream.CopyTo(requestStream, 1024 * 1024);
    }

    try
    {
        // Fails with 401 Signature doesn’t match
        var httpResponse = (WebResponse)request.GetResponse();
    }
    catch (WebException e)
    {
        using (WebResponse response = e.Response)
        {
            HttpWebResponse httpResponse = (HttpWebResponse)response;
            Console.WriteLine("Error code: {0}", httpResponse.StatusCode);
            using (Stream data = response.GetResponseStream())
            using (var reader = new StreamReader(data))
            {
                string text = reader.ReadToEnd();
                Console.WriteLine(text);
            }
        }
    }
}

 

As you can see credentials are hardcoded, but for production application it would probably be more secure and for flexibility's sake, to keep them in external configuration file. Also line #9 may require adjustments if you're going to send big batches to API. Now it's allowed to send JSON up to 100000 bytes in size.

Also, error handling is very simple - if {OPEN} API throws an error, details are printed out in a console window. In server environment such an event should be logged somewhere instead, rather outputted to stdout.

 

The above procedure can be invoked as follows:

// CCUv2 by URL
var urlv2 = new PurgeObjectV2() { Objects = new[] { "https://www.example.com/products/123456/" }, Domain = "production", Type = "arl", Action = "remove" };
purge(urlv2);

// CCUv2 by CpCode
var cpcodev2 = new PurgeObjectV2() { Objects = new[] { "12345" }, Domain = "production", Type = "cpcode", Action = "remove" };
purge(cpcodev2);

// CCUv3 by URL
var urlv3 = new PurgeObjectV3() { Objects = new[] { "/products/123456/" }, Hostname = "www.example.com" };
purge(urlv3);

 

Final sample code:

using Akamai.EdgeGrid.Auth;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Net;
using System.Text;

namespace purge_api_sample
{
    class Program
    {
        public class PurgeObject
        {
        }

        public class PurgeObjectV2 : PurgeObject
        {
            [JsonProperty("objects")]
            public string[] Objects { get; set; }
            [JsonProperty("type")]
            public string Type { get; set; }
            [JsonProperty("action")]
            public string Action { get; set; }
            [JsonProperty("domain")]
            public string Domain { get; set; }
        }

        public class PurgeObjectV3 : PurgeObject
        {
            [JsonProperty("objects")]
            public string[] Objects { get; set; }
            [JsonProperty("hostname")]
            public string Hostname { get; set; }
        }

        static void Main(string[] args)
        {
            // CCUv2 by URL
            var urlv2 = new PurgeObjectV2() { Objects = new[] { "https://www.example.com/products/123456/" }, Domain = "production", Type = "arl", Action = "remove" };
            purge(urlv2);

            // CCUv2 by CpCode
            var cpcodev2 = new PurgeObjectV2() { Objects = new[] { "12345" }, Domain = "production", Type = "cpcode", Action = "remove" };
            purge(cpcodev2);

            // CCUv3 by URL
            var urlv3 = new PurgeObjectV3() { Objects = new[] { "/products/123456/" }, Hostname = "www.example.com" };
            purge(urlv3);

            Console.ReadLine();
        }

        static void purge(PurgeObject purgeObject)
        {
            const String client_token = "akab-*********************************";
            const String access_token = "akab-*********************************";
            const String secret = "*********************************";
            const String api_url = "https://akab-*********************************.purge.akamaiapis.net";

            ClientCredential credentials = new ClientCredential(client_token, access_token, secret);
            EdgeGridV1Signer signer = new EdgeGridV1Signer(null, 100000);

            Uri uri = new Uri(api_url + "/ccu/v3/invalidate/url/production");
            if (purgeObject is PurgeObjectV2)
                uri = new Uri(api_url + "/ccu/v2/queues/default");

            WebRequest request = WebRequest.Create(uri);
            request.Method = "POST";
            request.ContentType = "application/json";
            ServicePointManager.Expect100Continue = false;

            // convert string to stream
            byte[] byteArray = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(purgeObject));
            MemoryStream uploadStream = new MemoryStream(byteArray);

            // Signs the current request using the Akamai C# library. Creates an Authentication header for the request.
            request = signer.Sign(request, credentials, uploadStream);

            if (uploadStream == null)
                request.ContentLength = 0;
            else if (uploadStream.CanSeek)
                request.ContentLength = uploadStream.Length;
            else if (request is HttpWebRequest)
                ((HttpWebRequest)request).SendChunked = true;

            if (uploadStream != null)
            {
                // avoid internal memory allocation before buffering the output
                if (request is HttpWebRequest)
                    ((HttpWebRequest)request).AllowWriteStreamBuffering = false;

                using (Stream requestStream = request.GetRequestStream())
                using (uploadStream)
                    uploadStream.CopyTo(requestStream, 1024 * 1024);
            }

            try
            {
                // Fails with 401 Signature doesn’t match
                var httpResponse = (WebResponse)request.GetResponse();
            }
            catch (WebException e)
            {
                using (WebResponse response = e.Response)
                {
                    HttpWebResponse httpResponse = (HttpWebResponse)response;
                    Console.WriteLine("Error code: {0}", httpResponse.StatusCode);
                    using (Stream data = response.GetResponseStream())
                    using (var reader = new StreamReader(data))
                    {
                        string text = reader.ReadToEnd();
                        Console.WriteLine(text);
                    }
                }
            }
        }
    }
}

 

Hope the above helps to start your journey with C# and {OPEN} API.

1 person found this helpful

Attachments

    Outcomes