How to implement a DelegatingHandler for X-HTTP-Method-Override in Web API

Take advantage of a DelegatingHandler and the X-HTTP-Method-Override in Web API to overcome browser and firewall constraints

How to implement a DelegatingHandler for X-HTTP-Method-Override in Web API
Thinkstock

When deploying your REST Web API over a public domain, you will sometimes encounter problems that are related to support for HTTP verbs. The two challenges in this regard are the limited support for HTTP verbs in old web browsers (i.e. they support only HTTP GET and HTTP POST) and aggressive firewalls that block traffic that is not either an HTTP GET or an HTTP POST. How will your application support a PUT or DELETE in these cases? Here’s exactly where the X-HTTP-Method-Override HTTP header comes to the rescue.

The X-HTTP-Method-Override HTTP header works somewhat similar to a hack. You can add the header with a value of either PUT or DELETE when invoking your Web API via JavaScript or via an XMLHttpRequest object from a web browser using an HTTP POST call. You can then have a delegating handler intercept the HTTP method to be invoked and take the appropriate actions.

In this article I will discuss how we can use a delegating handler in front of the request-response pipeline to alter the request to send a valid message to our application, or alter the response to send back a valid response to the client.

HTTP verbs and delegating handlers

If we are constrained to using only the HTTP verbs GET and POST due to limitations imposed by your client, the web browser, or the firewall fronting your web application, we will have to implement a workaround to support PUT and DELETE. This workaround typically involves adding the X-HTTP-Method-Override HTTP header to the request that specifies the verb we want to use within the HTTP POST call. In addition, we need a delegating handler in our application that checks for the header and, if it exists, makes the call to the HTTP method you want to be invoke.

Before we dive into the implementation, let’s take a quick look at what delegating handlers are and why we would use one here. A delegating handler and other message handlers are executed early in the request processing pipeline. These are classes that accept HTTP requests and return an HTTP response. Delegating handlers are similar to HttpModules in ASP.Net. But unlike HttpModules, delegating handlers can be chained: One delegating handler can reference another delegating handler. You can learn more about delegating handlers from my previous article, “How to work with message handlers in Web API.”

Create a Web API controller

Assume that you have a Web API controller similar to this:

public class AuthorsController : ApiController
    {
        // GET: api/authors
        public IEnumerable<string> Get()
        {
            return new string[] { “Joydip”, “Kanjilal” };
        }
        // GET: api/authors/1
        public string Get(int id)
        {
            return “Joydip Kanjilal”;
        }
        // POST api/author
        public void Post([FromBody]Author value) { }
        // PUT api/author/1
        public void Put(int id, [FromBody]Author value) { }
        // DELETE api/author/1
        public void Delete(int id) { }
    }

Create a DelegatingHandler for X-HTTP-Method-Override

Now let’s implement an X-HTTP-Method-Override handler. This is a message handler, so as usual it should extend the DelegatingHandler class.

public class CustomMessageHandler : DelegatingHandler
    {
        readonly string[] httpMethodsList = { “DELETE”, “HEAD”, “PUT” };
        const string httpMethodOverrideheader ="X-HTTP-Method-Override";
        protected override Task<HttpResponseMessage> SendAsync(             HttpRequestMessage request, CancellationToken cancellationToken)
        {
            if (request.Method == HttpMethod.Post && request.Headers.Contains(httpMethodOverrideheader))
            {               
                var httpMethod = request.Headers.GetValues(httpMethodOverrideheader).FirstOrDefault();
                if (httpMethodsList.Contains(httpMethod, StringComparer.InvariantCultureIgnoreCase))
                {                  
                    request.Method = new HttpMethod(httpMethod);
                }
            }
            return base.SendAsync(request, cancellationToken);
        }
    }

The code is quite self-explanatory. It checks for an HTTP POST having the X-HTTP-Method-Override header. If the header is in the list of methods, the request method is changed.

Register the DelegatingHandler

The next step is to register the handler. You can do this by adding this new handler to the MessageHandlers collection in WebApiConfig class as shown in the code snippet below.

public static void Register(HttpConfiguration config)
{
    config.MessageHandlers.Add(new CustomMessageHandler ());
     // Web API routes
    config.MapHttpAttributeRoutes();
     config.Routes.MapHttpRoute(
        name: “DefaultApi”,
        routeTemplate: “api/{controller}/{id}”,
        defaults: new { id = RouteParameter.Optional }
    );
}

Alternatively, you can register the delegating handler using the Application_Start event handler in the Global.asax.cs file as shown below.

protected void Application_Start(object sender, EventArgs e)
        {
            RegisterRoutes(RouteTable.Routes);
            GlobalConfiguration.Configuration.MessageHandlers.Add(new CustomMessageHandler());
        }

That’s all you have to do on the server side. On the client side, i.e., from the web browser, you should make sure that you add the override header as shown in the code snippet below.

$.ajax({
  url: “http://localhost:9820/api/Authors/1”,
  type: “POST”,
  data: JSON.stringify(authorData),
  headers: {
      “Content-Type”: “application/json”,
      “X-HTTP-Method-Override”: “PUT” },
})

As you can see in the previous code snippet, all you need to do is specify the HTTP method you want to invoke in the request header—X-HTTP-Method-Override : DELETE or X-HTTP-Method-Override : PUT— and then make a POST call to your resource.

Copyright © 2018 IDG Communications, Inc.