X-HTTP-Method-Override daje NotFound (404) na ASP.NET Web API

Usiłuję zaimplementować nadpisanie metody HTTP zgodnie z opisanymi krokamitutaj. Zasadniczo tworzę DelegatingHandler, podobny do następującego, i dodając go jako obsługę komunikatówApplication_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);
    }
}

Mam następujące metody zdefiniowane na moim kontrolerze:

persons/{id}, OTRZYMAĆpersons/{id}, POŁOŻYĆpersons/{id}, KASOWAĆ

Mogę nazywać je metodami „rodzimymi” i działają zgodnie z oczekiwaniami. Jednak gdy próbuję wywołać je przez POST, wysyłamX-HTTP-Method-Override nagłówek z „DELETE” lub „PUT”, dajeNie znaleziono (404) błąd. Ważne jest, aby dodać, że gdy daje ten błąd, nigdy nie osiągaMethodOverrideHandler - Umieściłem punkt przerwania, który nigdy nie jest trafiony; uderza w punkt przerwania, gdy wywołam normalne DELETE i PUT.

Próbowałem nawet dodać inną metodę:

persons/{id}, POST

Kiedy to robię, dostajęMetoda niedozwolona (405) zamiast.

Pomyślałem, że programy obsługi komunikatów zostały uruchomione PRZED dyspozytorami routingu i kontrolera. Dlaczego to daje mi 404?

Nie sądzę, aby było to powiązane, ale nie używam domyślnego routingu Web API. Zamiast tego mapuję za pomocą niestandardowego atrybutu przypisanego do każdej metody, tak jak poniżej:

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)
{
    // ...
}

EDYTOWAĆ: GetHttpMethodConstraint kod jest poniżej.

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;
    }
}

questionAnswers(2)

yourAnswerToTheQuestion