X-HTTP-Method-Override在ASP.NET Web API上提供NotFound(404)

ale*_*ete 7 c# asp.net-web-api

我正在尝试按照此处描述的步骤实现HTTP方法覆盖.基本上,我正在创建一个DelegatingHandler,类似于以下内容,并将其添加为消息处理程序Application_Start.

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我在Controller上定义了以下方法:

  • persons/{id},GET
  • persons/{id},PUT
  • persons/{id},删除

我可以通过他们的"原生"方法打电话给他们,他们按预期工作.但是,当我尝试通过POST调用它们,发送X-HTTP-Method-Override标题为"DELETE"或"PUT"时,它会给出Not Found(404)错误.重要的是要补充一点,当它给出这个错误时,它永远不会到达MethodOverrideHandler- 我已经放了一个永远不会被击中的断点; 当我调用正常的DELETE和PUT时它确实击中了断点.

我甚至尝试添加另一种方法:

  • persons/{id},POST

当我这样做时,我得到一个不允许方法(405).

我认为消息处理程序是在路由和控制器调度程序之前运行的.为什么这给我404?

我不认为这是相关的,但我没有使用默认的Web API路由.相反,我使用自定义属性进行映射,分配给每个方法,如下所示:

routes.MapHttpRoute(
    String.Format("{0}_{1}", operation.Name, service.ServiceId),
    String.Format("{0}/{1}", service.RoutePrefix, routeTemplateAttribute.Template),
    defaults,
    new { httpMethod = GetHttpMethodConstraint(operation) });
Run Code Online (Sandbox Code Playgroud)
[HttpDelete, RouteTemplate("persons/{id}")]
public HttpResponseMessage DeletePerson(string id)
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)

编辑:GetHttpMethodConstraint代码如下.

private static HttpMethodConstraint GetHttpMethodConstraint(MethodInfo methodInfo)
{
    var methodResolver = HttpMethodResolver.FromMethodInfo(methodInfo);
    return new HttpMethodConstraint(methodResolver.Resolve());
}
Run Code Online (Sandbox Code Playgroud)
internal class HttpMethodResolver
{
    private MethodInfo _methodInfo;

    private HttpMethodResolver(MethodInfo methodInfo)
    {
        _methodInfo = methodInfo;
    }

    public static HttpMethodResolver FromMethodInfo(MethodInfo methodInfo)
    {
        return new HttpMethodResolver(methodInfo);
    }

    public string[] Resolve()
    {
        var verbs = new List<HttpMethod>();

        if (MethodHasAttribute<HttpGetAttribute>())
        {
            verbs.Add(HttpMethod.Get);
        }
        else if (MethodHasAttribute<HttpPostAttribute>())
        {
            verbs.Add(HttpMethod.Post);
        }
        else if (MethodHasAttribute<HttpDeleteAttribute>())
        {
            verbs.Add(HttpMethod.Delete);
        }
        else if (MethodHasAttribute<HttpPutAttribute>())
        {
            verbs.Add(HttpMethod.Put);
        }
        else
        {
            throw new ServiceModelException("HTTP method attribute should be used");
        }

        return verbs.Select(v => v.Method).ToArray();
    }

    private bool MethodHasAttribute<T>() where T : Attribute
    {
        return GetMethodAttribute<T>() != null;
    }

    private T GetMethodAttribute<T>() where T : Attribute
    {
        return _methodInfo.GetCustomAttributes(typeof(T), true).FirstOrDefault() as T;
    }
}
Run Code Online (Sandbox Code Playgroud)

tug*_*erk 0

我尝试重现您的错误,但未能成功。在这里,您可以使用消息处理程序下载我的简单项目:https://dl.dropbox.com/u/20568014/WebApplication6.zip

我想指出消息处理程序在执行操作选择逻辑之前运行。因此,就您的情况而言,可能是其他原因导致了问题,我认为您应该查看其他消息处理程序、消息处理程序的注册代码等,因为问题的发生是由于您的消息处理程序从未运行。

另外,我认为您的IRouteConstraint实施GetHttpMethodConstraint对我来说看起来很可疑。

这是我的消息处理程序的注册代码:

protected void Application_Start(object sender, EventArgs e) {

    var config = GlobalConfiguration.Configuration;
    config.Routes.MapHttpRoute(
        "DefaultHttpRoute",
        "api/{controller}/{id}",
        new { id = RouteParameter.Optional }
    );

    config.MessageHandlers.Add(new MethodOverrideHandler());
}
Run Code Online (Sandbox Code Playgroud)