在ASP.NET MVC中是否可以实现X-HTTP-Method-Override?

5
我正在使用ASP.NET MVC实现一个RESTful API的原型,除了偶尔出现的一些错误外,我已经达到了开始时设定的所有要求,但是调用者无法使用X-HTTP-Method-Override自定义头来覆盖HTTP方法。
我希望以下请求能够实现...
GET /someresource/123 HTTP/1.1
X-HTTP-Method-Override: DELETE

如果有多个实现某个操作的方法,并且它们被标记为不同的 [AcceptVerbs] 属性,那么发往我的控制器方法的请求将被分派到实现 DELETE 功能而不是 GET 功能的方法上。因此,考虑以下两种方法,我希望上述请求被分派到第二种方法:

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

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

有人知道这是否可能吗?要做这件事需要多少工作量呢...

8个回答

5
由于[AcceptVerbs]属性与请求的实际HTTP动词绑定在一起,因此您将无法直接使用它。幸运的是,[AcceptVerbs]属性非常简单;您可以在http://www.codeplex.com/aspnet/SourceControl/changeset/view/21528#266431上查看源代码。

简而言之,子类化AcceptsVerbsAttribute并覆盖IsValidForRequest()方法。实现应该像以下示例:

string incomingVerb = controllerContext.HttpContext.Request.Headers["X-HTTP-Method-Override"] ?? controllerContext.HttpContext.Request.Method;
return Verbs.Contains(incomingVerb, StringComparer.OrdinalIgnoreCase);

我不得不从AcceptVerbsAttribute中复制代码并实现自己的代码,因为它是密封的。但是这种方法非常完美、简单,看起来你可以获得300分! - Greg Beech
嗨,Levi,我的ASP.net IIS或MVC正在拒绝DELETE和PUT请求,有什么办法可以让它们工作吗?我尝试将Allow: PUT,DELETE放入IIS Allow头中,但它不起作用... - DucDigital
Duc,我知道已经很久很久很久了,但请检查一下是否安装了WebDav。如果安装了,请卸载它。否则,它会拦截您的PUT和DELETE请求。 - cwharris

3

Levi的回答很好。另外,在自定义AcceptsVerbsAttribute中添加了一个检查,还要检查FORM集合,这样您就可以简单地放置一个隐藏输入来触发DELETE(类似于MVC 2的Html.HttpMethodOverride(HttpVerbs.Delete))。

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

将incomingVerb赋值更改为:

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

注意这种方法!请参阅Stephen Walther的相关文章

希望这能帮助到某些人。


另一个可能的解决方案是将其作为 MIME 类型参数发送到内容类型标头中。 - inf3rno

3

表单中的插入:

<%= Html.HttpMethodOverride(HttpVerbs.Delete) %>

2

这段对话有点旧,但是我想分享一下我使用mvc 2时发现的内容:

浏览器支持两种HTTP动词:GET和POST,但是ASP.NET MVC 2允许您使用Html.HttpMethodOverride帮助方法来模拟Put、Get和Delete。在内部,这是通过将动词发送到X-HTTP-Method-Override表单字段来实现的。HttpMethodOverride的行为被[AcceptVerbs]属性以及新的更短的动词属性所使用:

例如,操作声明:

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

您应该承担起您的GET请求,其中X-HTTP-Method-Override设置为Delete。


不仅限于表单字段,还可以在标题或查询字符串中。思想是支持HTTP方法覆盖。 +1 - Robert Koritnik

2
我很惊讶这还没有被提到,但是ASP.NET MVC本地支持X-HTTP-Method-Override,至少从版本2开始就一直如此。无需编写自定义代码来处理此问题。
它的工作方式如下:
在AcceptVerbsAttribute中(也由[HttpPut],[HttpPost]等代理),有一个IsValidForRequest方法。在该方法内部,它使用Request.GetHttpMethodOverride()进行检查,该方法返回以下条件的正确覆盖HTTP方法:
覆盖仅受POST请求支持。所有其他请求都会被忽略。
如果X-HTTP-Method-Override值为GET或POST,则忽略它。这是有道理的,因为您永远不需要使用这些值进行覆盖。
它按以下优先顺序查找X-HTTP-Method-Override:
1)HTTP标头
2)表单正文
3)查询字符串
如果您真的很好奇,这是GetHttpMethodOverride()的外观(来自MVC 3的源代码):
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;
    }
}

1

你看过Simply Restful Routing吗?它已经实现了这个功能。

编辑于2010年2月:方法重写已内置于MVC 2中。


我没有看到它实现X-HTTP-Method-Override的任何信息,快速扫描源代码也没有发现。你确定吗?如果是这样,请指出它实现在哪个文件中?谢谢! - Greg Beech
X-HTTP-Method-Override是一个人设计的支持非GET/POST请求的方法。而简单REST路由则是另一种方法。不同的设计,相同的目标。 - Craig Stuntz
我其实不想将REST与RPC风格的URL结合在一起(我很满意其中一个,但更愿意避免两者的组合),因此我认为我宁愿选择标题选项而不是像Simply Restful设计中的动作链接。 - Greg Beech

0

X-HTTP-Method-Override 是自定义的头信息,很可能不被您的 Web 容器所支持。

您是从网页调用此方法吗?如果是的话,您应该使用 XmlHttpRequest 来使用 DELETE(或其他您想要的动词)。更好的方式是,使用 JS 框架来为您处理繁重的工作。


我不是在调用它,而是在实现它... 我想让这个实现支持自定义头。 - Greg Beech
我不明白。如果你控制着实现过程,为什么不使用标准动词按照HTTP的设计来使用它呢? - Kevin
2
因为不幸的是,一些客户端、代理和防火墙不允许除GET或POST之外的动词,所以有时人们需要能够用GET伪造DELETE和用POST伪造PUT。这有点糟糕,但大多数RESTful服务似乎支持此标头。 - Greg Beech
将GET映射到其他内容是危险的。 GET应该是无副作用的。 我们只应该覆盖POST。 - Miguel Madero

0
你可以创建一个实现了 OnActionExecuting 的 ActionFilter,在控制器动作被调用之前触发。然后,你可以根据请求头的值进行重定向,并基于 X-HTTP-Method-Override 头部的值进行判断。

那么此时它不是已经为它认为要调用的操作执行了参数绑定吗?这些方法可能具有不同的参数。我想也许我需要在分派程序选择操作之前挂钩进去? - Greg Beech

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接