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.
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
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 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);
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
.
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:
App_Code
folder of a website projectThis 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.