5

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...?

bignose
  • 30,281
  • 14
  • 77
  • 110
Greg Beech
  • 133,383
  • 43
  • 204
  • 250

8 Answers8

5

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
  • 32,628
  • 3
  • 87
  • 88
  • 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 Jan 29 '09 at 11:26
  • 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 Nov 19 '09 at 02:53
  • Duc, I know it's been a LONG LONG LONG LONG time, but see if WebDav is installed. If it is, uninstall it. Otherwise, it'll intercept your PUT and DELETE requests. – cwharris Mar 12 '13 at 17:04
3

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
  • 178
  • 9
3

Insert to Form:

<%= Html.HttpMethodOverride(HttpVerbs.Delete) %>
sth
  • 222,467
  • 53
  • 283
  • 367
2

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:

  • Overriding is only supported in POST requests. All others are ignored.
  • If the X-HTTP-Method-Override value is GET or POST, it's ignored. This makes sense, as you'd never need to override with these values.
  • It looks for X-HTTP-Method-Override in the following places in this priority: 1) HTTP Header 2) Form Body 3) Query String

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;
    }
}
Johnny Oshika
  • 54,741
  • 40
  • 181
  • 275
2

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
  • 2,333
  • 21
  • 23
1

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
  • 125,891
  • 12
  • 252
  • 273
  • 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 Jan 22 '09 at 20:24
  • 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 Jan 22 '09 at 21:32
  • 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 Jan 22 '09 at 22:18
0

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.

Roman Marusyk
  • 23,328
  • 24
  • 73
  • 116
Kevin
  • 30,111
  • 9
  • 76
  • 83
  • I'm not calling it, I'm implementing it... And I want to make the implementation support the custom header. – Greg Beech Jan 21 '09 at 23:59
  • 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 Jan 22 '09 at 01:30
  • 2
    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 Jan 22 '09 at 08:47
  • It's dangerous to map GET to anything else. GET should be side effect free. We should only override POSTs – Miguel Madero Oct 13 '12 at 15:08
0

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.

Roman Marusyk
  • 23,328
  • 24
  • 73
  • 116
Jeffrey Meyer
  • 5,410
  • 7
  • 30
  • 27
  • 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 Jan 22 '09 at 08:56