Scott Hanselman

HTTP PUT or DELETE not allowed? Use X-HTTP-Method-Override for your REST Service with ASP.NET Web API

February 05, 2014 Comment on this post [21] Posted in ASP.NET Web API
Sponsored By

I got an email today where someone had built a REST(ful/ish) API with ASP.NET Web API that had a customer who was against the idea of using GET, POST, PUT, and DELETE, and insisted that they only use GET and POST.

Sometimes this is because of a browser or client limitaton, sometimes it's a really tense corporate firewall. They wanted to know what they could do.

One thing you can do is to "tunnel" HTTP Methods inside another HTTP Header. Basically you have a header that says "No, seriously, I know I got here via a POST, but use this one instead." You would still POST, but then you'd have "X-HTTP-Method-Override:PUT" as a header.

Here is a PUT in the Postman REST client:

image

So that's:

PUT /api/Person/4 HTTP/1.1
Host: localhost:10320
Content-Type: application/json
Cache-Control: no-cache

And here's the same PUT, except as a POST plus an X-HTTP-Method-Override header.

image

Raw, that's like this:

POST /api/Person/4 HTTP/1.1
Host: localhost:10320
Content-Type: application/json
X-HTTP-Method-Override: PUT
Cache-Control: no-cache

Now, how do you get ASP.NET Web API to respect this new way to route things? You may have a Web API Controller like this:

public IEnumerable<Person> Get() { }

// GET api/person/5
public Person Get(int id) { }

// POST api/person
public void Post([FromBody]Person value) { }

// PUT api/person/5
public void Put(int id, [FromBody]Person value) { }

// DELETE api/person/5
public void Delete(int id) { }

And you likely don't want to change it. Make a MethodOverrideHandler like this one. You can add the code yourself, get it from a NuGet package, or use one from the WebAPIContrib project. It's up to you.

public class MethodOverrideHandler : DelegatingHandler
{
readonly string[] _methods = { "DELETE", "HEAD", "PUT" };
const string _header = "X-HTTP-Method-Override";

protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// Check for HTTP POST with the X-HTTP-Method-Override header.
if (request.Method == HttpMethod.Post && request.Headers.Contains(_header))
{
// Check if the header value is in our methods list.
var method = request.Headers.GetValues(_header).FirstOrDefault();
if (_methods.Contains(method, StringComparer.InvariantCultureIgnoreCase))
{
// Change the request method.
request.Method = new HttpMethod(method);
}
}
return base.SendAsync(request, cancellationToken);
}
}

You see it checks if it's a post, looks for the extra header, then changes the request's Method property after the message has been received, but before it's been sent through the pipeline. It'll show up on the right method just as if a PUT had been sent, because from its perspective, a PUT was sent.

You need to register this new MethodOverrideHandler in your WebApiConfig like this, just by adding to the MessageHandlers collection, next to the rest of the configuration and routing code.

public static void Register(HttpConfiguration config)
{
config.MessageHandlers.Add(new MethodOverrideHandler());

//OTHER REGULAR STUFF HERE

// Web API routes
config.MapHttpAttributeRoutes();

config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}

On the client side, you can keep sending a post with your .ajax call in jQuery, for example, just make sure the override header in there.

$.ajax({
url: "http://localhost:10320/api/Person/4",
type: "POST",
data: JSON.stringify(whatever),
headers: {
"Content-Type": "application/json",
"X-HTTP-Method-Override": "PUT" },
})

That's the general idea, enjoy!


Sponsor: Big Thanks to Aspose for sponsoring the blog this week! Aspose.Total for .NET has all the APIs you need to create, manipulate and convert Microsoft Office documents and a host of other file formats in your applications. Curious? Start a free trial today.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service
February 05, 2014 6:08
You know it's funny Scott, but we've relied on the ability to do this very thing with MVC, and it was missing in MVC5. We had to upgrade to MVC 5.1 in order to get it working again. http://stackoverflow.com/a/19811998/304832
Dan
February 05, 2014 6:32
I think this solution can be much cleaner by using a reverse-proxy that does this verb translation. So that this translation would become a matter of deployment. I can develop the same API for all the customers who like/dislike PUT/HEAD/DELETE. And for customers who want to restrict PUT/HEAD/DELETE, all their API deployments can be behind the same reverse-proxy.
February 05, 2014 6:34
Thanks for sharing the idea. Best wishes
February 05, 2014 8:27
Good post. Why do you only consider the web API Restful(ish)? Can you make a post about that?
February 05, 2014 12:33
Cool solution Scott. I really like how easy it is to extend WebAPI like this.

I did something similar to fix URL generation when the WebAPI service is behind an SSL terminating load balancer.
February 05, 2014 13:30
Hey Scott,

Thanks for sharing this. I remember similar implementations for WCF REST using custom endpoint behaviors. Although, I'm wondering why doesn't Web API (and also WCF REST) support this "out of the box"? The "X-HTTP-Method-Override" is a well-known (and widely used, not only in the context of Web API) method, so making this a built-in functionality would certainly make our lives a bit easier.

Cheers!
February 05, 2014 17:00
But wouldn't a Firewall Admin who was zealous enough to block DELETE|PUT block the X-HTTP-* headers too?
February 05, 2014 17:36
To be fair, this is dirtying up RESTfull-ness...

In which case, why not just have a polymorhpic POST handler on the WEB API side that can do UPSERT-y type behavior?
February 05, 2014 17:43
Interesting tool to put into the toolbox for when a creative solution is needed. Thanks.
February 05, 2014 17:46
Cool. Did not know that was even possible
February 05, 2014 17:54
@Adrian - in addition to the scenario of admins blocking headers, some browsers won't allow custom headers in certain situations (e.g. CORS with I.E. 8). Using a query string parameter instead of a header would be the way to go.
February 05, 2014 18:29
This is a workaround for over zealous blocking rules. The real solution is revisiting the blockage preventing the correct methods (admin / firewall / etc) rather than dirtying RESTfull-ness by trying to get the correct method in 'under the radar'.
February 05, 2014 18:40
Good to know this option is out there, but how silly is it to have to do this? "No, we don't allow the word "PUT" to come down over _our_ wire if you put it here in the string, but if you have it over in this other place, no harm done." I'm no OPs guy, but seriously, what's the point?
February 06, 2014 12:13
it's also been part of WebApiContrib since 2 years ago :)

https://github.com/WebApiContrib/WebAPIContrib/blob/master/src/WebApiContrib/MessageHandlers/MethodOverrideHandler.cs
February 06, 2014 13:46
Similar to Daniel above, what the hell is the point in doing this?
Can any one shed any light; is there any legitimate reason for these verbs to be blocked? Or are said admin's just being asshats?

I'd be saying to the customer (words to the effect): "well we can re-work the design of the application to support the fact that your IT guys want to be asshats but it's going to cost you £X for us to do and it will take Y amount of time. Alternatively you can ask them to remove a pointless restriction which will take 5 minutes and cost nothing, it's up to you."
February 07, 2014 8:35
@Daniel and @Peter. Best practice is to reduce your attack surface. Google "HTTP Verb Tampering". It is the reason why IIS disables put and delete by default and you explicitly have to enable them. With that said if you have a legitimate need/use case your security guy should be able to create exceptions for your application.
February 08, 2014 0:17
@Stephen, Yeah, yeah. I get it. But if OPs or a host is blocking PUT/DELETE when it's needed, all they've done is disable "HTTP Verb Tampering" while still allowing "X-HTTP-Method-Override tampering".
February 08, 2014 0:41
You are all correct, this is lame, but it's the reality in many large (stupid) enterprises. I'm not saying it's good, nor am I saying it's recommended. It simply IS.
February 08, 2014 18:59
@Scott it is interesting, lame or not. I am glad you shared.

@Daniel agreed.
February 10, 2014 9:46
In a decade, there will be a post discussing how abusing this has led to breaking the web in some way.
April 26, 2014 15:43
Thanks @Scott, it save me hours of debugging a production app.

Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.