Adding HTTP HEAD support to ASP.NET Web API

HEAD HTTP verb is defined in RFC 2616 as “identical to GET except that the server MUST NOT return a message-body in the response.” As such you can think of it as a twin brother of GET.

There are a lot of use cases for HEAD: pinging without the overhead of transferring data, or simply requesting information about the size of a resource (which can be used to provide download progress bar), just to name a few.

Unfortunately, out of the box, ASP.NET Web API doesn’t provide a mechanism of supporting HEAD or coupling GET & HEAD.

We can work around that.

How is it now?

You’d think it might be as simple as adding [AcceptVerbs(“HEAD”)] to your action?

Unfortunately, in the default Verb-based dispatching model, Web API will not find such action for a HEAD request and respond with 404.

If you use RPC dispatching:

Your odds are little better, because the action will be found and hit correctly, but:
– you probably don’t want to ditch RESTful routes just for HEAD support
– the behavior of the HEAD will not be complete anyway, because it will respond with Content-Length: 0 rather then the content length of the resource it represents.

HTTP HEAD trickery with MessageHandler

MessageHandlers, due to their nature of working closely with HTTP request/response and running first & last in the pipeline, are a perfect tool to solve this problem for us.

As a HEAD request flows into the system, we can switch it from HEAD to GET, and it will be processed as such. Then, at the end of the pipeline, we will just strip out the body (content) of the response.

The implementation would look like this:

So if we have an incoming HEAD request, we switch it to GET and save a flag inside HttpRequestMessage. Once the entire processing pipeline runs, we try to retrieve the flag from the request and if it’ there, we reset the content of the response to string.Empty.

We then copy the content headers from old HttpContent to the new one as they would be nullfied as soon as new HttpContent is set. This can be done very easily in a loop because HttpContentHeaders is actually an IEnumerable<KeyValuePair<string, IEnumerable<string>>> and as such can be iterated through.

The only exception to the copying is the Content-Length header which the framework will compute for us based on the HttpContent. Since we just set it to string.Empty that would obviously be 0 which is not desirable. We can work around that by reading the byte length of the original body and set that manually.

As a result we end up with a complete response to a GET, including Content-Length and Content-Type – but without the content body.

This can now be registered against the configuration – and voila – you have HEAD support for all your GET methods.

And that’s it.

Share the post!

  • Mario Ienasescu

    I could probably figure out this myself, but how would you extend this if you’d want to use a HttpHeadAttribute to decorate your action method?

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1312()

  • Micah Smith

    I don’t know. Normally I use HEAD when I want to ping (as you mentioned) and not incur any processing penalties. What was your motivation for letting the controller execute?

    • Filip W

      According to HTTP spec you *must* provide content-length and all headers relevant for the regular GET, which in reality means that you have to process anyway.

      HEAD is a GET, without data flowing over the network

      • Micah Smith

        Ahhh. Makes sense then. Thanks!

  • Pingback: Scott Banwart's Blog › Distributed Weekly 198()

  • GroovBird

    There’s something missing here. If the controller returns a response other than 200 OK and there’s no content, there will be a NullReferenceException.

  • Jan Van der Haegen

    Holy bleep! Thank you so much, works as a charm!!