November 28, 2018

KGSoft.TinyHttpClient: A Smarter Way to Consume Your APIs

I have built countless client-side applications, and I would say that over 95% of them needed to consume an API in some form or another. We would kick the projects off, read the API documentation, and create a simple HTTP helper utility. With each project that went by, we became more elaborate with these HTTP helpers, each with better ideas than its previous version. We added features like generic type deserialization, callback functions, inherent logging, global header config, ADAL capability, etc. Enter KGSoft.TinyHttpClient!

It was until recently that I had an epiphany. Partly because the HTTP helpers we were writing seemed to come to a plateau where they were generic enough to be used in most applications, whilst still remaining powerful and useful. It was then that I set off to create KGSoft.TinyHttpClient. My aim for this library was to handle the mundane things we do around HTTP connectivity in our applications and ultimately minimize the code that gets rewritten in almost every single project. My secondary aim was to correctly implement HttpClient (by not disposing it, even though .NET has it incorrectly implementing IDisposable – it is re-entrant and thread-safe by design).

Typical Use Cases For KGSoft.TinyHttpClient

First and foremost, you will need to install KGSoft.TinyHttpClient by downloading the nuget package.

Calling and Endpoint

An important thing to note about this library is that we have encapsulated the response from an HTTP call in a Response and Response<T> object. Response<T> is used when we are expecting a result of T back from the call, and Response is when we are not.

One of the key features of this library is the generic type deserialisation. Almost all methods have an override for calls where we are expecting a type to be returned. For example (not expecting an object in the response):

Response r = await Helper.PostAsync("some http endpoint", "{ some json object }");

An example of a situation where we are expecting “SomeObject” in the response:

Response<SomeObject> r = await Helper.PostAsync<SomeObject>("some http endpoint", "{ some json object }");

// r.Result - This is the deserialised object of type SomeObject

In this instance, we are returned a Response<T>. On this Response object, there is a Result property of type T. Pretty nifty eh? It’s as easy as that!

Global HTTP Configuration

Most of the time, there are certain authentication headers we will need to supply in order to authenticate with the API we are calling. Traditionally, we would have to supply these with every HTTP request we wanted to make. KGSoft.TinyHttpClient makes this much simpler, by allowing you to set your headers once, in the global static config. This global static config can be accessed by manipulating the members on the HttpConfig class, like so:

HttpConfig.DefaultAuthHeader = new AuthenticationHeaderValue("Bearer", "XYZ");

HttpConfig.CustomHeaders = new Dictionary<string, string>() { { "CustomKey", "CustomValue" } };

These headers will then be included in every request, unless custom headers are specified by the request itself, as will be explained in the next section.

Overriding Default Global Headers

There may be times where we do not want to use our globally defined headers for each and every request. We could be calling multiple APIs, each with different authentication mechanisms. Fear not, as every HttpRequest method has the ability to provide its own, unique headers, as can be seen below:

HeaderConfig myOtherHeaders = new HeaderConfig() 
{ 
   AuthHeader = new AuthenticationHeaderValue("Bearer", "XYZ"); 
};

Response<SomeObject> r = await Helper.PostAsync<SomeObject>("some http endpoint", "{ some json object }", config: myOtherHeaders );

We can simply provide a HeaderConfig object to any of the methods, as an optional parameter.

Using Pre-Request Action for
Refreshing/Acquiring Access Tokens

In a few of our projects, we needed to use Microsoft ADAL authentication. When using the Microsoft.IdentityModel.Clients.ActiveDirectory library, you will become aware that it will acquire a token, as well as attempt to refresh it if possible. The result of this will likely need to be added to your headers. With KGSoft.TinyHttpClient, we can simply specify an action to be executed before every request, ensuring that we always have a valid token. If a token was unable to be acquired, a UI will be presented to the user to prompt for re-authentication. This can be defined in the HttpConfig as follows:

/// <summary>
/// Our method to get and set our ADAL AccessToken
/// </summary>
/// <returns></returns>
private async Task GetAuthBearerToken()
{
    var authority = "";
    var resource = "";
    var clientId = "";
    var redirectUri = "";
    var extraQueryParams = "";

    var authContext = new AuthenticationContext(authority);
    PlatformParameters platform = new PlatformParameters(PromptBehavior.Auto);

    // Call to the ADAL to get a token
    var result = await authContext.AcquireTokenAsync(
         resource,
         clientId,
         new Uri(redirectUri),
         platform,
         UserIdentifier.AnyUser,
         extraQueryParams);

    if (result != null) // We get a token, and we set it as the AuthHeader for our HttpClient in the global config
        HttpConfig.DefaultAuthHeader = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", result.AccessToken);
}


[TestMethod]
public async Task Test_PreAuth()
{
    // Set the PreRequestAuth Function here and forget about it
    HttpConfig.PreRequestAuthAsyncFunc = GetAuthBearerToken;
    var response = await Helper.GetAsync("some protected API");
}

Now, we can be assured that we will always have a correct and valid token available to us before any HTTP calls are made.

Upcoming Features

On the roadmap for this library, I would like to include a 401 callback. This will enable us to specify an action in the event of a 401 being received from an API we are calling. I feel that this is a useful addition, as there are wildly varying ways that this is dealt with in client-application developemnt.

Outro

I hope that you get as much use from this library as I have. If there are features you feel are missing, please comment or open an issue on my Github.

Happy coding!

KG

You may also like...

Leave a Reply