An Abstract Boilerplate HttpHandler

0 comments suggest edit

My last project involved writing a lot of HttpHandlers to respond to HTTP requests originating from a cell phone. To simplify my life, I created an abstract base handler that handled a lot of the repetitive tasks in writing an HTTP handler.

So today, I read Scott Hanselman’s post about the boilerplate HttpHandler he uses. He says one day he’ll get more organized and make an abstract base class to handle this kind of boilerplate stuff.

I’ve got your back Scott.

I went ahead and took my base class and quickly (about 10 minutes) incorporated some of the things he has in his boilerplate and voila! An abstract base class! Enjoy.

/// <summary>
/// An abstract base Http Handler for all your
/// <see cref="IHttpHandler"/> needs.
/// </summary>
/// <remarks>
/// <p>
/// For the most part, classes that inherit from this
/// class do not need to override <see cref="ProcessRequest"/>.
/// Instead implement the abstract methods and
/// properties and put the main business logic
/// in the <see cref="HandleRequest"/>.
/// </p>
/// <p>
/// HandleRequest should respond with a StatusCode of
/// 200 if everything goes well, otherwise use one of
/// the various "Respond" methods to generate an appropriate
/// response code.  Or use the HttpStatusCode enumeration
/// if none of these apply.
/// </p>
/// </remarks>
public abstract class BaseHttpHandler : IHttpHandler
{
    /// <summary>
    /// Creates a new <see cref="BaseHttpHandler"/> instance.
    /// </summary>
    public BaseHttpHandler() {}
 
    /// <summary>
    /// Processs the incoming HTTP request.
    /// </summary>
    /// <param name="context">Context.</param>
    public void ProcessRequest(HttpContext context)
    {
        SetResponseCachePolicy(context.Response.Cache);
 
        if(!ValidateParameters(context))
        {
            RespondInternalError(context);
            return;
        }
 
        if(RequiresAuthentication
            && !context.User.Identity.IsAuthenticated)
        {
            RespondForbidden(context);
            return;
        }
 
        context.Response.ContentType = ContentMimeType;
 
        HandleRequest(context);
    }
 
    /// <summary>
    /// Indicates whether or not this handler can be
    /// reused between successive requests.
    /// </summary>
    /// <remarks>
    /// Return true if this handler does not maintain
    /// any state (generally a good practice).  Otherwise
    /// returns false.
    /// </remarks>
    public bool IsReusable
    {
        get
        {
            return true;
        }
    }
 
    /// <summary>
    /// Handles the request.  This is where you put your
    /// business logic.
    /// </summary>
    /// <remarks>
    /// <p>This method should result in a call to one 
    /// (or more) of the following methods:</p>
    /// <p><code>context.Response.BinaryWrite();</code></p>
    /// <p><code>context.Response.Write();</code></p>
    /// <p><code>context.Response.WriteFile();</code></p>
    /// <p>
    /// <code>
    /// someStream.Save(context.Response.OutputStream);
    /// </code>
    /// </p>
    /// <p>etc...</p>
    /// <p>
    /// If you want a download box to show up with a 
    /// pre-populated filename, add this call here 
    /// (supplying a real filename).
    /// </p>
    /// <p>
    /// </p>
    /// <code>Response.AddHeader("Content-Disposition"
    /// , "attachment; filename=\"" + Filename + "\"");</code>
    /// </p>
    /// </remarks>
    /// <param name="context">Context.</param>
    public abstract void HandleRequest(HttpContext context);
 
    /// <summary>
    /// Validates the parameters.  Inheriting classes must
    /// implement this and return true if the parameters are
    /// valid, otherwise false.
    /// </summary>
    /// <param name="context">Context.</param>
    /// <returns><c>true</c> if the parameters are valid,
    /// otherwise <c>false</c></returns>
    public abstract bool ValidateParameters(HttpContext context);
 
    /// <summary>
    /// Gets a value indicating whether this handler
    /// requires users to be authenticated.
    /// </summary>
    /// <value>
    ///    <c>true</c> if authentication is required
    ///    otherwise, <c>false</c>.
    /// </value>
    public abstract bool RequiresAuthentication {get;}
 
    /// <summary>
    /// Gets the content MIME type.
    /// </summary>
    /// <value></value>
    public abstract string ContentMimeType {get;}
 
    /// <summary>
    /// Sets the cache policy.  Unless a handler overrides
    /// this method, handlers will not allow a respons to be
    /// cached.
    /// </summary>
    /// <param name="cache">Cache.</param>
    public virtual void SetResponseCachePolicy
        (HttpCachePolicy cache)
    {
        cache.SetCacheability(HttpCacheability.NoCache);
        cache.SetNoStore();
        cache.SetExpires(DateTime.MinValue);
    }
 
    /// <summary>
    /// Helper method used to Respond to the request
    /// that the file was not found.
    /// </summary>
    /// <param name="context">Context.</param>
    protected void RespondFileNotFound(HttpContext context)
    {
        context.Response.StatusCode 
            = (int)HttpStatusCode.NotFound;
        context.Response.End();
    }
 
    /// <summary>
    /// Helper method used to Respond to the request
    /// that an error occurred in processing the request.
    /// </summary>
    /// <param name="context">Context.</param>
    protected void RespondInternalError(HttpContext context)
    {
        // It's really too bad that StatusCode property
        // is not of type HttpStatusCode.
        context.Response.StatusCode =
            (int)HttpStatusCode.InternalServerError;
        context.Response.End();
    }
 
    /// <summary>
    /// Helper method used to Respond to the request
    /// that the request in attempting to access a resource
    /// that the user does not have access to.
    /// </summary>
    /// <param name="context">Context.</param>
    protected void RespondForbidden(HttpContext context)
    {
        context.Response.StatusCode 
            = (int)HttpStatusCode.Forbidden;
        context.Response.End();
    }
}

You can also download the file from http://tools.veloc-it.com/

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

16 responses

  1. Avatar for Thomas Wagner
    Thomas Wagner March 18th, 2005

    Very nice job Phil !



    TW

  2. Avatar for wow
    wow April 4th, 2005

    guys like you have way too much free time, dude.

  3. Avatar for hooh
    hooh May 22nd, 2006

    thanks!This article is ok!
    And I have a problem ,may you give me answers?
    Recently I have to realize uploading big files( 500M) in my web application(asp.net),so I must fast check the file size to decide continue or not.Can not use javascript (Filesystem Activex) to check the file size.so I want to check the file using httphandler.Before the page load,to get the Request.conentlength to decide the file size ,if the file size is more then 500M the response redirect to annother page. if the file size is less than 500M ,continue to save the file. But if the file >500M ,I can not to redirect to other page ,something error?
    public void ProcessRequest(HttpContext context)
    {
    if (context.Request.ContentLength < 500 * 1024 *1024)
    {

    context.Response.Redirect("~/test1.aspx");
    }
    else
    {
    context.Response.Redirect("~/test2.aspx");
    }
    }

  4. Avatar for christopher baus
    christopher baus November 3rd, 2006

    Phil,
    Out of curiosity do you have a VS 2005 project template that you use for creating HTTPHandlers?
    Thanks,
    Chris

  5. Avatar for Haacked
    Haacked November 3rd, 2006

    Nope, but that's a really great idea!

  6. Avatar for Alex
    Alex March 4th, 2007

    Shouldn't the isReusable property be virtual?

  7. Avatar for Haacked
    Haacked March 4th, 2007

    You don't need to declare it virtual when implementing an interface. It's "virtual" by virtue of the interface.

  8. Avatar for stock market for beginners
    stock market for beginners December 27th, 2009

    i rarely ever see abstracts anymore. i was one of the few in my graduating CIS class that fully believed in using them, so it's nice to see other's do so in the professional world :)

  9. Avatar for Cecil P
    Cecil P February 4th, 2010

    I found this to be a good addition too where applicable.

    aspadvice.com/...

  10. Avatar for Michael Stum
    Michael Stum February 8th, 2010

    Thanks for this! One addition would be to make IsReusable virtual so that it can be overridden to false if required.

  11. Avatar for Michael Stum
    Michael Stum February 8th, 2010

    I just saw your answer regarding IsReusable being virtual. I do not know if that is still the case, as I get an error when I use it in a derived class, telling me I should use "new" as I'm hiding it, and override gives a compilation error.

  12. Avatar for Richard
    Richard February 9th, 2010

    IsReusable does need to be virtual in order to override it. The only other approach is to re-implement the interface and use an explicit implementation of the property.
    Also, since the class is abstract, the constructor should be protected.

  13. Avatar for Freek Felling
    Freek Felling August 23rd, 2010

    Great article.
    The download doesn't work anymore.
    Seems like tools.veloc-it.com doesn't exist.

  14. Avatar for Chris Porter
    Chris Porter September 6th, 2012

    I am implementing a combination of this class and Hanselman's Boilerplate HttpHandler class.
    Is there a reason that you are returning a 500 (InternalServerError) when the parameters of the request are invalid rather than a 400 (BadRequest)? All the documentation I'm finding online show that the purpose of the 400 response code is "you didn't ask the right question, please check your request and try again" while the 500 response code is for "you asked a question and I was unable to answer it, sorry". I'm going to go the 400 route but was hoping to find out why you went the 500 route.
    Thanks for the resource though!!

  15. Avatar for Bernd_meier
    Bernd_meier December 30th, 2012

     is there a vb.net version for that ?

    greets

    Bernd Meier
    Http://www.blackhatlinks.com

  16. Avatar for Sampathraj
    Sampathraj December 29th, 2016

    Great article.
    The download doesn't work anymore.
    Seems like tools.veloc-it.com doesn't exist. Please share active download link.