X-HTTP-Method-Override fornisce NotFound (404) su API Web ASP.NET

Sto cercando di implementare il metodo override HTTP seguendo i passaggi descritti qui . Fondamentalmente, sto creando un DelegatingHandler, simile al seguente, e aggiungendolo come gestore di messaggi su Application_Start .

 public class MethodOverrideHandler : DelegatingHandler { readonly string[] _methods = { "DELETE", "HEAD", "PUT" }; const string _header = "X-HTTP-Method-Override"; protected override Task 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); } } 

Ho i seguenti metodi definiti sul mio controller:

  • persons/{id} , GET
  • persons/{id} , PUT
  • persons/{id} , ELIMINA

Posso chiamarli con i loro metodi “nativi” e funzionano come previsto. Tuttavia, quando provo a chiamarli tramite un POST, inviando l’intestazione X-HTTP-Method-Override con “DELETE” o “PUT”, viene visualizzato un errore Not Found (404) . È importante aggiungere che, quando dà questo errore, non raggiunge mai il MethodOverrideHandler – Ho messo un Breakpoint che non viene mai colpito; colpisce il punto di interruzione quando chiamo normale DELETE e PUT.

Ho anche provato ad aggiungere un altro metodo:

  • persons/{id} , POST

Quando lo faccio, ottengo invece un metodo non consentito (405) .

Pensavo che i gestori di messaggi fossero eseguiti PRIMA dei dispatcher di Routing e Controller. Perché questo mi dà 404?

Non penso che questo sia correlato, ma non sto usando il routing dell’API Web predefinito. Invece, sto mappando usando un attributo personalizzato, assegnato a ciascun metodo, come questo:

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

EDIT : il codice GetHttpMethodConstraint è sotto.

 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(); if (MethodHasAttribute()) { verbs.Add(HttpMethod.Get); } else if (MethodHasAttribute()) { verbs.Add(HttpMethod.Post); } else if (MethodHasAttribute()) { verbs.Add(HttpMethod.Delete); } else if (MethodHasAttribute()) { verbs.Add(HttpMethod.Put); } else { throw new ServiceModelException("HTTP method attribute should be used"); } return verbs.Select(v => v.Method).ToArray(); } private bool MethodHasAttribute() where T : Attribute { return GetMethodAttribute() != null; } private T GetMethodAttribute() where T : Attribute { return _methodInfo.GetCustomAttributes(typeof(T), true).FirstOrDefault() as T; } } 

Ho provato a riprodurre il tuo errore ma non ci sono riuscito. Qui, puoi scaricare il mio semplice progetto con il tuo gestore di messaggi: https://dl.dropbox.com/u/20568014/WebApplication6.zip

Vorrei sottolineare che i gestori dei messaggi vengono eseguiti prima che venga eseguita la logica di selezione delle azioni. Quindi, nel tuo caso, probabilmente qualcos’altro causa il problema e penso che dovresti guardare gli altri tuoi gestori di messaggi, il codice di registrazione del gestore di messaggi, ecc. Perché il problema si verifica a causa del fatto che il tuo gestore di messaggi non viene mai eseguito.

Inoltre, penso che la tua implementazione GetHttpMethodConstraint , GetHttpMethodConstraint , mi sembri sospetta.

Ecco il mio codice di registrazione per il gestore di messaggi:

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

Penso di avere lo stesso problema. Sembra che i vincoli del percorso siano controllati prima di qualsiasi gestore di messaggi.

Così ho creato un vincolo personalizzato che sa di verificare un metodo HTTP sottoposto a override:

 class OverrideableHttpMethodConstraint : HttpMethodConstraint { public OverrideableHttpMethodConstraint(HttpMethod httpMethod) : base(httpMethod) { } protected override bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) { IEnumerable 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); } }