【问题标题】:X-HTTP-Method-Override gives NotFound (404) on ASP.NET Web APIX-HTTP-Method-Override 在 ASP.NET Web API 上给出 NotFound (404)
【发布时间】:2013-07-15 19:24:41
【问题描述】:

我正在尝试按照here 描述的步骤实现 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);
    }
}

我的控制器上定义了以下方法:

  • persons/{id},获取
  • persons/{id},PUT
  • persons/{id},删除

我可以通过它们的“本地”方法调用它们,它们按预期工作。但是,当我尝试通过 POST 调用它们,发送带有“DELETE”或“PUT”的 X-HTTP-Method-Override 标头时,它会给出 Not Found (404) 错误。重要的是要补充一点,当它给出这个错误时,它永远不会到达MethodOverrideHandler——我已经放置了一个永远不会命中的断点;当我调用正常的 DELETE 和 PUT 时,它确实会到达断点。

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

  • persons/{id},发帖

当我这样做时,我得到一个 Method Not Allowed (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) });
[HttpDelete, RouteTemplate("persons/{id}")]
public HttpResponseMessage DeletePerson(string id)
{
    // ...
}

编辑GetHttpMethodConstraint 代码如下。

private static HttpMethodConstraint GetHttpMethodConstraint(MethodInfo methodInfo)
{
    var methodResolver = HttpMethodResolver.FromMethodInfo(methodInfo);
    return new HttpMethodConstraint(methodResolver.Resolve());
}
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;
    }
}

【问题讨论】:

  • 您是否尝试将您的操作方法从DeletePerson 更改为Delete
  • 我在下面回答了,但是如果您可以压缩您的应用程序并将其放在某个地方,我可以看看,因为我无法重现您的问题。
  • 仅作记录,我的问题已通过这个答案得到澄清:stackoverflow.com/a/13959148/1288760

标签: c# asp.net-web-api


【解决方案1】:

我想我也有同样的问题。看起来确实在任何消息处理程序之前检查了路由约束。

所以我创建了一个自定义约束,它知道检查被覆盖的 HTTP 方法:

class OverrideableHttpMethodConstraint : HttpMethodConstraint
{
    public OverrideableHttpMethodConstraint(HttpMethod httpMethod) : base(httpMethod)
    {
    }

    protected override bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection)
    {
        IEnumerable<string> headerValues;
        if (request.Method.Method.Equals("POST", StringComparison.OrdinalIgnoreCase) && 
            request.Headers.TryGetValues("X-HTTP-Method-Override", out headerValues))
        {
            var method = headerValues.FirstOrDefault();
            if (method != null)
            {
                request.Method = new HttpMethod(method);
            }
        }

        return base.Match(request, route, parameterName, values, routeDirection);
    }
}

【讨论】:

    【解决方案2】:

    我已尝试重现您的错误,但无法重现。在这里,您可以使用消息处理程序下载我的简单项目: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());
    }
    

    【讨论】:

    • 感谢您的回答!我很确定GetHttpMethodConstraint 工作正常,因为我可以使用它们的“本机”动词成功调用声明为“PUT”和“DELETE”的方法。仅当我使用 POST 和方法覆盖标头调用时才会出现问题。我曾尝试在 que 路由映射之前和之后注册消息处理程序,但它不起作用。我还删除了我注册的另一个消息处理程序,以查看它是否导致问题,但没有运气。我可以确认您的应用程序可以正常工作,因此它与基础设施或其他任何东西无关。
    • @alextercete GetHttpMethodConstraint 在消息处理程序之前启动。我的钱是你的问题与GetHttpMethodConstraint有关。
    • 我在问题上添加了GetHttpMethodConstraint 的代码。我觉得没什么特别的……
    • 我奖励你为你的答案所做的努力。但是,由于我仍然没有解决我的问题,因此我将其置之不理。还是谢谢!
    猜你喜欢
    • 2010-12-21
    • 2010-10-02
    • 1970-01-01
    • 1970-01-01
    • 2015-09-22
    • 2017-12-10
    • 2012-05-18
    • 2014-10-18
    • 1970-01-01
    相关资源
    最近更新 更多