Pretty URLs for web handlers
28th of March, 2017 0 comments

Pretty URLs for web handlers

Considering that Kentico is based on ASP.NET WebForms, ASHX handlers are present on most websites. From file and attachment retrieval, script and style resources, to other custom handlers, they are one of the easiest solutions for custom API endpoints and similar requirements.

If you need to spice up the routes of your existing ASHX handlers, or learn a trick for any future ASHX missions - keep reading.

Why bother with routes?

Apart from obvious SEO reasons, my adventure began as a requirement to improve the caching of files served by a handler. Some content delivery networks and cache services do not deal with query strings that well. /CMSPages/MySite/GetSpecialResource.ashx?id=abc123 had to change to something nicer: /specialresource/abc123

There's an assembly attribute for that

ASHX handlers are implemented with a code-behind that contains a class implementing the IHttpHandler interface.

Instead of creating an .ashx file, we can use the RegisterHttpHandler assembly attribute to register the handler and define its route.

using System.Web;
using CMS.Helpers;
using CMS.Routing.Web;

[assembly: RegisterHttpHandler("myhandler/{myparameter}", typeof(MyHandler), Order = 1)]
public class MyHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        var myParameter = QueryHelper.GetString("myparameter", "");

        context.Response.StatusCode = 200;
        context.Response.Write("OK!");
    }

    public bool IsReusable
    {
        get { return true; }
    }
}

The route

The first parameter of the assembly attribute is the route. Parameters are defined in braces, and can be accessed as query strings.

"myhandler/{myparameter}"

var myParameter = QueryHelper.GetString("myparameter", "");

As the code documentation of the handler states, it supports guid and int constraints. This will ensure that the parameter is of the correct type - otherwise, the route will not be matched.

"myhandler/{myparameter:int}"

// This is matched: /myhandler/5
// This is not matched: /myhandler/hello

var myParameter = QueryHelper.GetInteger("myparameter", 0);

The order

Handlers can have many routes registered to them. Using the Order parameter, you can define the correct order in which the routes should be matched.

[assembly: RegisterHttpHandler("myhandler/prefix-{otherparameter}", typeof(MyHandler), Order = 1)]
[assembly: RegisterHttpHandler("myhandler/{myparameter}", typeof(MyHandler), Order = 2)]

If a request to /myhandler/hello is made, the myparameter query string will contain the value hello. In the case of /myhandler/prefix-hello, the otherparameter query string will contain the value hello.

Handlers must be in a compiled file

In website projects, .ashx handlers can have the IHttpHandler implementation inside the .ashx file.

To utilise the RegisterHttpHandler assembly attribute, the handler must be in a compiled file:

  • In a separate assembly, or
  • As a compiled file in a Kentico web application, or
  • In the App_Code folder of a website project

If you can - just switch to WebAPI

This is just a solution for simple endpoints. It only takes minutes to set up a WebAPI project in your Kentico solution, and it's much cleaner and more flexible than creating a large number of handlers.

Written by Kristian Bortnik


Tags

Comments