Reputation: 136727
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...?
Upvotes: 6
Views: 11592
Reputation: 5470
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.
Upvotes: 0
Reputation: 30449
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.
Upvotes: 0
Reputation: 57592
I'm surprised that this hasn't been mentioned yet, but ASP.NET MVC natively supports X-HTTP-Method-Override and has been doing so from at least version 2. There's no need to write custom code to handle this.
It work in the following way:
Inside AcceptVerbsAttribute (also proxied by [HttpPut], [HttpPost], etc), there's an IsValidForRequest method. Inside that method, it checks with Request.GetHttpMethodOverride(), which returns the proper overriden HTTP method with the following conditions:
If you're really curious, here's how GetHttpMethodOverride() looks (from MVC 3's source code):
public static class HttpRequestExtensions {
internal const string XHttpMethodOverrideKey = "X-HTTP-Method-Override";
public static string GetHttpMethodOverride(this HttpRequestBase request) {
if (request == null) {
throw new ArgumentNullException("request");
}
string incomingVerb = request.HttpMethod;
if (!String.Equals(incomingVerb, "POST", StringComparison.OrdinalIgnoreCase)) {
return incomingVerb;
}
string verbOverride = null;
string headerOverrideValue = request.Headers[XHttpMethodOverrideKey];
if (!String.IsNullOrEmpty(headerOverrideValue)) {
verbOverride = headerOverrideValue;
}
else {
string formOverrideValue = request.Form[XHttpMethodOverrideKey];
if (!String.IsNullOrEmpty(formOverrideValue)) {
verbOverride = formOverrideValue;
}
else {
string queryStringOverrideValue = request.QueryString[XHttpMethodOverrideKey];
if (!String.IsNullOrEmpty(queryStringOverrideValue)) {
verbOverride = queryStringOverrideValue;
}
}
}
if (verbOverride != null) {
if (!String.Equals(verbOverride, "GET", StringComparison.OrdinalIgnoreCase) &&
!String.Equals(verbOverride, "POST", StringComparison.OrdinalIgnoreCase)) {
incomingVerb = verbOverride;
}
}
return incomingVerb;
}
}
Upvotes: 2
Reputation: 2341
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.
Upvotes: 2
Reputation: 219
Insert to Form:
<%= Html.HttpMethodOverride(HttpVerbs.Delete) %>
Upvotes: 3
Reputation: 126587
Have you looked at Simply Restful Routing? It already does this.
Edited Feb 2010 to add: Method overrides are built into MVC 2.
Upvotes: 1
Reputation: 198
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.
Upvotes: 3
Reputation: 32828
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);
Upvotes: 5