views:

1046

answers:

7

I'm implementing a prototype of a RESTful API using ASP.NET MVC and apart from the odd bug here and there I've achieve all the requirements I set out at the start, apart from callers being able to use the X-HTTP-Method-Override custom header to override the HTTP method.

What I'd like is that the following request...

GET /someresource/123 HTTP/1.1
X-HTTP-Method-Override: DELETE

...would be dispatched to my controller method that implements the DELETE functionality rather than the GET functionality for that action (assuming that there are multiple methods implementing the action, and that they are marked with different [AcceptVerbs] attributes). So, given the following two methods, I would like the above request to be dispatched to the second one:

[ActionName("someresource")]
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult GetSomeResource(int id) { /* ... */ }

[ActionName("someresource")]
[AcceptVerbs(HttpVerbs.Delete)]
public ActionResult DeleteSomeResource(int id) { /* ... */ }

Does anybody know if this is possible? And how much work would it be to do so...?

A: 

The X-HTTP-Method-Override is a custom header and most likely isn't supported by your web container.

Are you calling this from a web page? If so, you should probably use XmlHttpRequest with DELETE (or whatever verb you want). Better yet, use a JS framework to do the heavy lifting for you.

Kevin
I'm not calling it, I'm implementing it... And I want to make the implementation support the custom header.
Greg Beech
I don't understand. If you control the implementation, why on earth would you not use HTTP the way it was designed by using the standard verbs?
Kevin
Because unfortunately some clients, proxies and firewalls do not allow verbs other than GET or POST, so sometimes people need to be able to fake DELETE with GET and PUT with POST. It's a bit lame, but most RESTful services seem to support this header.
Greg Beech
A: 

You could create an ActionFilter that implements OnActionExecuting, which fires before the controller action is invoked. You could then interrogate the request headers, and redirect based on the value of the X-HTTP-Method-Override header, when present.

Jeffrey Meyer
At that point won't it have already done the parameter binding for the action it thought it was going to call though? The methods may have different parameters. I think that maybe I need to hook in before the action is selected by the dispatched?
Greg Beech
+1  A: 

Have you looked at Simply Restful Routing? It already does this.

Edited Feb 2010 to add: Method overrides are built into MVC 2.

Craig Stuntz
I don't see anything about it implementing X-HTTP-Method-Override and a quick scan of the source doesn't reveal anything. Are you sure about this? If so could you point me to the file in which it's implemented in? Cheers!
Greg Beech
X-HTTP-Method-Override is one person's design for supporting non-GET/POST requests. Simply Restful Routing is another. Different design, same goal.
Craig Stuntz
I don't really want to combine REST with RPC style URLs (I'm happy with one or the other, but would prefer to avoid a combination of both) so I think I'd rather go with the header option than with action links as in the Simply Restful design.
Greg Beech
+3  A: 

You won't be able to use the [AcceptVerbs] attribute as-is since it's tied to the request's actual HTTP verb. Fortunately the [AcceptVerbs] attribute is very simple; you can see the source for yourself at http://www.codeplex.com/aspnet/SourceControl/changeset/view/21528#266431.

In short, subclass AcceptsVerbsAttribute and override the IsValidForRequest() method. The implementation would be something like the following:

string incomingVerb = controllerContext.HttpContext.Request.Headers["X-HTTP-Method-Override"] ?? controllerContext.HttpContext.Request.Method;
return Verbs.Contains(incomingVerb, StringComparer.OrdinalIgnoreCase);
Levi
I had to nick the code from AcceptVerbsAttribute and implement my own one as unfortunately it's sealed, but the approach works perfectly and is nice and simple, so looks like you get the 300 points!
Greg Beech
hi levi, however my ASP.net IIS or MVC is denying DELETE and PUT request, is there anyway to get this work? I tried to put Allow: PUT, DELETE into IIS Allow header but it won't work...
DucDigital
+1  A: 

Levi's answer is great. Additionally, I added a check in the custom AcceptsVerbsAttribute that also examines the FORM collection, so you can simply put a hidden input to trigger the DELETE (similar to MVC 2's Html.HttpMethodOverride(HttpVerbs.Delete)).

<input name="X-HTTP-Method-Override" type="hidden" value="DELETE" />

Change the incomingVerb assignment to:

string incomingVerb = controllerContext.HttpContext.Request.Headers["X-HTTP-Method-Override"] ?? controllerContext.HttpContext.Request.Form["X-HTTP-Method-Override"] ??controllerContext.HttpContext.Request.HttpMethod;

Be careful with this approach! See a related post by Stephen Walther.

Hopefully this helps someone.

xeb
+2  A: 

Insert to Form:

<%= Html.HttpMethodOverride(HttpVerbs.Delete) %>
Tomasz Leszczynski
A: 

This conversation is a bit old, but I wanted to share what I have found using mvc 2:

Browsers support two HTTP verbs: GET and POST, but ASP.NET MVC 2 allows you to simulate Put, Get, and Delete using Html.HttpMethodOverride helper method. Internally, this works by sending the verb in an X-HTTP-Method-Override form field. The behavior of HttpMethodOverride is used by the [AcceptVerbs] attribute as well as the new shorter verb attributes:

For example, the action declaration:

[ActionName("someresource")]
[HttpDelete]
public ActionResult DeleteSomeResource()

should take responsibility for your get request that has the X-HTTP-Method-Override set to Delete.

Merritt