Scott Hanselman

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

February 5, '14 Comments [20] 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. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Wednesday, February 05, 2014 2:08:15 AM UTC
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
Wednesday, February 05, 2014 2:32:57 AM UTC
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.
Manoj Agarwal
Wednesday, February 05, 2014 2:34:16 AM UTC
Thanks for sharing the idea. Best wishes
Imtiaz
Wednesday, February 05, 2014 4:27:27 AM UTC
Good post. Why do you only consider the web API Restful(ish)? Can you make a post about that?
Adam Christopher
Wednesday, February 05, 2014 8:33:53 AM UTC
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.
Wednesday, February 05, 2014 9:30:20 AM UTC
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!
Wednesday, February 05, 2014 1:00:37 PM UTC
But wouldn't a Firewall Admin who was zealous enough to block DELETE|PUT block the X-HTTP-* headers too?
Adrian
Wednesday, February 05, 2014 1:36:17 PM UTC
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?
Will
Wednesday, February 05, 2014 1:43:44 PM UTC
Interesting tool to put into the toolbox for when a creative solution is needed. Thanks.
Vinny Brown
Wednesday, February 05, 2014 1:46:09 PM UTC
Cool. Did not know that was even possible
Taki
Wednesday, February 05, 2014 1:54:47 PM UTC
@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.
Wednesday, February 05, 2014 2:29:22 PM UTC
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'.
Wednesday, February 05, 2014 2:40:04 PM UTC
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?
Thursday, February 06, 2014 8:13:32 AM UTC
it's also been part of WebApiContrib since 2 years ago :)

https://github.com/WebApiContrib/WebAPIContrib/blob/master/src/WebApiContrib/MessageHandlers/MethodOverrideHandler.cs
Thursday, February 06, 2014 9:46:15 AM UTC
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."
Peter
Friday, February 07, 2014 4:35:32 AM UTC
@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.
Stephen
Friday, February 07, 2014 8:17:05 PM UTC
@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".
Friday, February 07, 2014 8:41:56 PM UTC
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.
Saturday, February 08, 2014 2:59:36 PM UTC
@Scott it is interesting, lame or not. I am glad you shared.

@Daniel agreed.
Stephen
Monday, February 10, 2014 5:46:21 AM UTC
In a decade, there will be a post discussing how abusing this has led to breaking the web in some way.
Anon
Name
E-mail (will show your gravatar icon)
Home page
 
Comment (Some html is allowed: a@href@title, b, blockquote@cite, em, i, li, ol, pre, strike, strong, sub, super, u, ul) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.
Live Comment Preview

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