ASP.NET Web Forms Extensibility: Handler Factories

An handler factory is the class that implements IHttpHandlerFactory and is responsible for instantiating an handler (IHttpHandler) that will process the current request. This is true for all kinds of web requests, whether they are for ASPX pages, ASMX/SVC web services, ASHX/AXD handlers, or any other kind of file. Also used for restricting access for certain file types, such as Config, Csproj, etc.

Handler factories are registered on the global Web.config file, normally located at %WINDIR%\Microsoft.NET\Framework<x64>\vXXXX\Config for a given path and request type (GET, POST, HEAD, etc). This goes on section <httpHandlers>.

You would create a custom handler factory for a number of reasons, let me list just two:

  • A centralized place for using dependency injection;
  • Also a centralized place for invoking custom methods or performing some kind of validation on all pages.

Let’s see an example using Unity for injecting dependencies into a page, suppose we have this on Global.asax.cs:

   1: public class Global : HttpApplication
   2: {
   3:     internal static readonly IUnityContainer Unity = new UnityContainer();
   5:     void Application_Start(Object sender, EventArgs e)
   6:     {
   7:         Unity.RegisterType<IFunctionality, ConcreteFunctionality>();
   8:     }
   9: }

We instantiate Unity and register a concrete implementation for an interface, this could/should probably go in the Web.config file. Forget about its actual definition, it’s not important.

Then, we create a custom handler factory:

   1: public class UnityPageHandlerFactory : PageHandlerFactory
   2: {
   3:     public override IHttpHandler GetHandler(HttpContext context, String requestType, String virtualPath, String path)
   4:     {
   5:         IHttpHandler handler = base.GetHandler(context, requestType, virtualPath, path);
   7:         //one scenario: inject dependencies
   8:         Global.Unity.BuildUp(handler.GetType(), handler, String.Empty);
  10:         return (handler);
  11:     }
  12: }

It inherits from PageHandlerFactory, which is .NET’s included factory for building regular ASPX pages. We override the GetHandler method and issue a call to the BuildUp method, which will inject required dependencies, if any exist.

An example page with dependencies might be:

   1: public class SomePage : Page
   2: {
   3:     [Dependency]
   4:     public IFunctionality Functionality
   5:     {
   6:         get;
   7:         set;
   8:     }
   9: }

Notice the DependencyAttribute, it is used by Unity to identify properties that require dependency injection. When BuildUp is called, the Functionality property (or any other properties with the DependencyAttribute attribute) will receive the concrete implementation associated with it’s type, as registered on Unity.

Another example, checking a page for authorization. Let’s define an interface first:

   1: public interface IRestricted
   2: {
   3:     Boolean Check(HttpContext ctx);
   4: }

An a page implementing that interface:

   1: public class RestrictedPage : Page, IRestricted
   2: {
   3:     public Boolean Check(HttpContext ctx)
   4:     {
   5:         //check the context and return a value
   6:         return ...;
   7:     }
   8: }

For this, we would use an handler factory such as this:

   1: public class RestrictedPageHandlerFactory : PageHandlerFactory
   2: {
   3:     private static readonly IHttpHandler forbidden = new UnauthorizedHandler();
   5:     public override IHttpHandler GetHandler(HttpContext context, String requestType, String virtualPath, String path)
   6:     {
   7:         IHttpHandler handler = base.GetHandler(context, requestType, virtualPath, path);
   9:         if (handler is IRestricted)
  10:         {
  11:             if ((handler as IRestricted).Check(context) == false)
  12:             {
  13:                 return (forbidden);
  14:             }
  15:         }
  17:         return (handler);
  18:     }
  19: }
  21: public class UnauthorizedHandler : IHttpHandler
  22: {
  23:     #region IHttpHandler Members
  25:     public Boolean IsReusable
  26:     {
  27:         get { return (true); }
  28:     }
  30:     public void ProcessRequest(HttpContext context)
  31:     {
  32:         context.Response.StatusCode = (Int32) HttpStatusCode.Unauthorized;
  33:         context.Response.ContentType = "text/plain";
  34:         context.Response.Write(context.Response.Status);
  35:         context.Response.Flush();
  36:         context.Response.Close();
  37:         context.ApplicationInstance.CompleteRequest();
  38:     }
  40:     #endregion
  41: }

The UnauthorizedHandler is an example of an IHttpHandler that merely returns an error code to the client, but does not cause redirection to the login page, it is included merely as an example.

One thing we must keep in mind is, there can be only one handler factory registered for a given path/request type (verb) tuple. A typical registration would be:

   1: <httpHandlers>
   2:     <remove path="*.aspx" verb="*"/>
   3:     <add path="*.aspx" verb="*" type="MyNamespace.MyHandlerFactory, MyAssembly"/>
   4: </httpHandlers>

First we remove the previous registration for ASPX files, and then we register our own.

And that’s it. A very useful mechanism which I use lots of times.


No Comments