Kentico CMS Quick Tip: Minimal JSON Web APIs with IHttpHandler and .ashx Files
Tags: kentico, javascript, aspnet, cms
Published: 2019-10-28 14:00:21

---
title: Kentico CMS Quick Tip: Minimal JSON Web APIs with IHttpHandler and .ashx Files
published: true
description: Learn how to build a simple Web API in the CMS that you can call from JavaScript in the browser, with a single file.
tags: kentico, javascript, aspnet, cms
series: Kentico Quick Tips
cover_image: https://thepracticaldev.s3.amazonaws.com/i/i3ulb7hmgpqra7dl4wxi.jpg
---

<figcaption>Photo by <a href="https://unsplash.com/@bnwaneampeh">Benjamin Nwaneampeh</a> on <a href="https://unsplash.com">Unsplash</a></figcaption>

> This post was inspired by a discussion I had recently with Chris Bass, an inspiring member of the Kentico developer community. Check out [his blog](https://chrisbass.wakeflyexperts.com/blog) for more Kentico content.

## Kentico Portal Engine CMS + API

When we need to make calls to our [Kentico CMS Portal Engine](https://docs.kentico.com/k12sp/developing-websites/portal-engine-development-overview) web application from the browser over XHR or from another web service, we need an API to communicate with.

There are several ways to accomplish this, each with pros and cons, depending on our requirements 🤔.

> Here's [another great blog post](https://www.kenticotricks.com/blog/pretty-urls-for-web-handlers) about this topic from Kristian Bortnik.

---

### Web API 2 - The Sports Car 🏎

Kentico's documentation explains the steps for [integrating Web API 2](https://docs.kentico.com/k12sp/integrating-3rd-party-systems/using-asp-net-web-api-with-kentico) into the CMS.

The approach is great if you need a large and robust custom API surface standing in front of the CMS - and it's an approach I've used many, many times 👍.

However, the setup isn't trivial and the solution effectively runs the Web API OWIN-based application within the CMS - which leads to some sharp edges 🔪.

---

### Kentico REST API - Public Transportation 🚍

Kentico has a [REST API built-in](https://docs.kentico.com/k12sp/integrating-3rd-party-systems/kentico-rest-service/getting-data-using-rest), which can be used to query, and modify all kinds of data within the application 🧐.

It does provide security through a [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) HTTP header and but authenticates against the normal User accounts created within Kentico.

The REST API exposes the Kentico Page data and `*Info` objects directly, effectively creating a projection of the database over HTTP.

Given the above, the caveat of this built-in solution is that it's verbose, not customizable, and a leaky abstraction 😞.

---

### IHttpHandler - The Commuter Car 🚗

For those simple scenarios where we only need a handful of endpoints, exposing a limited set of data, curated and filtered for us, we would like a way to build an API... without all the API.

A nice solution to this problem is the ASP.NET `IHttpHandler`, which can be exposed through an `.ashx` file in our CMS project.

> You can read more about [how IHttpHandler works](https://docs.microsoft.com/en-us/previous-versions/bb398986(v=vs.140)?redirectedfrom=MSDN) in Microsoft's documentation.

`IHttpHandler` gives us extremely low-level to an incoming HTTP Request and the outgoing HTTP Response.

There's no WebForms code here, just the raw request and response 😮.

This is perfect for our use-case since we don't want to render HTML through a complex web of page lifecycle events and user controls 👏.

Let's take a look at some code for a concrete example of how all of this works.

> I see the `IHttpHandler` solution as ideal for small client-side integrations and also JSON-based server-to-server integrations with existing, in-production, Kentico CMS sites that can't risk big architectural changes.

> I've done both, but we'll look at the client-side solution today.

---

## Example: An E-Commerce Store with Dynamic Prices

Imagine we have a Business-to-Business (B2B) e-commerce application where the prices and inventory need to be pulled live from a back-end warehousing or [ERP](https://en.wikipedia.org/wiki/Enterprise_resource_planning) system (not Kentico).

We don't want to delay the loading of a product details page each time a visitor requests it because we need to fetch the pricing - that would hurt SEO and the User Experience ☹!

Instead, we want to cache the product details page and then, via JavaScript, request the price independently 😁.

So, we need a simple API endpoint that can forward this request on to the back-end system.

---

### Creating the .ashx File

Let's open our Kentico CMS solution, expand the project, and then the `CMSPages` folder.

Right-click on the `CMSPages` folder and select "Add" -> "Generic Handler".

![Visual Studio Project UI context menu for adding a Generic Handler](https://thepracticaldev.s3.amazonaws.com/i/nub8t8rcj8c821hftsmv.jpg)

We're going to name this handler `ProductApi` and Visual Studio will add the `.ashx` extension for us.

> IIS already knows how to process requests for `.ashx` files, so even if you haven't worked with this file type before, there's no extra work necessary to use it in your application 🤓.

What we end up with is a class named `ProductApi` that looks like the following:

```csharp
public class ProductApi : IHttpHandler
{

    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "text/plain";
        context.Response.Write("Hello World");
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}
```

---

### Handling the Request

Now we need to handle the incoming request from the browser.

We have the raw `HttpContext` to work with here. This is good, because we don't want the typical Web Forms infrastructure to get in our way, but it also means a bit more work for us 😑.

The `ProcessRequest` method is where we will do all of our work with the HTTP Request.

Let's assume that the browser is going to send an XHR request, with a `skuid` specified as a query string parameter, and it will expect a JSON response in return.

Here's `ProcessRequest` with some validation and error handling:

```csharp
public void ProcessRequest(HttpContext context)
{
    // Set this first
    context.Response.ContentType = "application/json";

    string method = context.Request.HttpMethod;

    if (!string.Equals(method, "GET", StringComparison.OrdinalIgnoreCase))
    {
        context.Response.StatusCode = 400;

        return;
    }

    string skuIdParam = context.Request.QueryString.Get("skuid");

    int skuId = ValidationHelper.GetInteger(skuIdParam, 0);

    if (skuId == 0)
    {
        context.Response.StatusCode = 400;

        return;
    }

    SKUInfo sku = SKUInfoProvider.GetSKUInfo(skuId);

    if (sku is null)
    {
        context.Response.StatusCode = 404;

        return;
    }

    // continue processing ...

```
---

### Creating the Response

Now that we have handled all the potential issues, we can get our back-end system identifier out of the `sku` object and request the most up-to-date values.

For our example we will pretend the response comes back in the following shape:

```csharp
public class ProductStats
{
    public decimal Price { get; set; }
    public int Inventory { get; set; }
}
```

Let's get hand-wavy 👋 here and assume we were successful in getting values back from our back-end system and we now want to send them back to the browser.

These last few steps are pretty simple:

1. Get the `ProductStats` response from the back-end system
1. Use `Newtonsoft.Json` to serialize the C# object into a JSON `string`
1. Write the JSON `string` as the HTTP response

```csharp
    // continue processing ...

    ProductStats response = // response from our back-end system

    string responseText = JsonConvert.SerializeObject(
        response, 
        serializationSettings);

    context.Response.Write(responseText);
}
```

You might have noticed the `serializationSettings` parameter above. That can be customized to your preferences and use-case, but it allows you to define how `Newtonsoft.Json` produces JSON from your C#.

I typically store this in a `static readonly` field in my `IHttpHandler`, and these are the settings I tend to use 😎:

```csharp
private static readonly JsonSerializerSettings serializationSettings = 
    new JsonSerializerSettings
    {
        Formatting = Formatting.None,
        ContractResolver = new CamelCasePropertyNamesContractResolver(),

        // UTC Date serialization configuration
        DateFormatHandling = DateFormatHandling.IsoDateFormat,
        DateParseHandling = DateParseHandling.DateTimeOffset,
        DateTimeZoneHandling = DateTimeZoneHandling.Utc,
        DateFormatString = "yyyy-MM-ddTHH:mm:ss.fffK",
    };
```
---

### Using Our API

So what does using this "API" look like?

Well, we can request the "API" in the browser like so:

![Browser displaying fake data JSON response from HttpHandler](https://thepracticaldev.s3.amazonaws.com/i/pgvzy5zyps9j5po7fhll.jpg)

But what about from JavaScript? Well, that's just as easy 😀!

> I'm going to assume we're supporting modern browsers, so legacy fallbacks are up to the reader 😏:

```js
(async () => {
    const params = new URLSearchParams({ skuid: 10 });

    const response = await fetch(`/CMSPages/ProductApi.ashx?${params}`);

    const { price, inventory } = await response.json();

    console.log('Price', price);
    console.log('Inventory', inventory);
})()
```

Who would have thought we could create a completely custom JSON based integration API in just a couple of minutes 🤗!?

---

## Bonus: All the Context

I would also like to note that since the HTTP request is going to the same domain that the JavaScript loaded under, there's no annoying cookie or CORS restrictions 🧐.

All cookies for the current domain are sent back to the server with every HTTP request, even XHR requests to our `.ashx` file.

This means that the normal Kentico `*Context` classes that give us access to the ambient request data, like the current authenticated user (`MembershipContext`) and the current shopping cart (`ShoppingCartContext`) are all still available in our `ProductApi` class ⚡.

If we want to respond with additional discounts for Customers in different groups, or send the current user's ID with the SKU Id to our back-end system to get product recommendations, we can do that too 😄!

How about displaying a shipping time estimate based on information gathered from the [Browser Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API) and the items in the shopping cart? Yup, we could do that 😃.

## Wrap Up

While Kentico's Web API 2 integration approach and the built-in REST API provide a lot of functionality, they don't quite fit the requirements for a small, custom, minimalist endpoint exposed by the CMS for XHR requests from the browser.

Fortunately, `IHttpHandler`s and `.ashx` files give us a quick and dirty way to stand up an endpoint using low-level ASP.NET features, without losing out on the functionality of the CMS 👍.

If you try this approach out, let me know what you think!

Thanks for reading 🙏!

---

If you are looking for additional Kentico content, checkout the Kentico tag here on DEV:

{% tag kentico %}

Or my Kentico blog series:

- [Kentico 12: Design Patterns](https://dev.to/search?q=Kentico%2012%3A%20Design%20Patterns)
- [Kentico CMS Quick Tips](https://dev.to/search?q=Kentico%20CMS%20Quick%20Tip)