2012-10-25 7 views
6

Ich versuche HTTP-Methodenüberschreibung nach den Schritten here zu implementieren implementieren. Im Grunde erstelle ich einen DelegatingHandler, ähnlich dem folgenden, und füge es als Nachrichtenhandler unter Application_Start hinzu.X-HTTP-Method-Override gibt NotFound (404) auf ASP.NET-Web-API

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

Ich habe die folgenden Methoden definiert auf meinem Controller:

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

ich anrufen kann sie durch ihre "native" Methoden und sie funktionieren wie erwartet. Wenn ich jedoch versuche, sie über einen POST anzurufen und den Header X-HTTP-Method-Override mit "DELETE" oder "PUT" zu senden, wird ein Fehler Not Found (404) angezeigt. Es ist wichtig hinzuzufügen, dass, wenn es diesen Fehler gibt, es niemals die MethodOverrideHandler erreicht - ich habe einen Haltepunkt gesetzt, der nie getroffen wird; Es trifft den Haltepunkt, wenn ich normale DELETE und PUT anrufe.

ich auch versucht, indem eine andere Methode:

  • persons/{id}, POST

Wenn ich das tue, erhalte ich eine Methode nicht erlaubt (405) statt.

Ich dachte, dass Nachrichtenhandler VOR dem Routing und Controller Dispatcher ausgeführt wurden. Warum gibt mir das 404?

Ich glaube nicht, dass dies verwandt ist, aber ich verwende nicht Standard-Web-API-Routing. Stattdessen bin ich Zuordnen eines benutzerdefinierten Attribut verwenden, um jede Methode zugewiesen, wie folgt aus:

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: GetHttpMethodConstraint Code ist unten.

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

Haben Sie versucht, Ihre Aktion-Methode von 'DeletePerson' zu' delete' zu ​​ändern? –

+0

Ich antwortete unten, aber wenn Sie Ihre Anwendung zippen und irgendwo ablegen können, kann ich einen Blick darauf werfen, da ich Ihr Problem nicht reprosieren konnte. – tugberk

+0

Nur zur Erinnerung, mein Problem wurde durch diese Antwort geklärt: http://stackoverflow.com/a/13959148/1288760. – alextercete

Antwort

0

habe ich versucht, Ihre Fehler zu reproduzieren, aber ich war nicht in der Lage. Hier können Sie mein einfaches Projekt mit Ihrem Message-Handler herunterladen: https://dl.dropbox.com/u/20568014/WebApplication6.zip

Ich möchte darauf hinweisen, dass Message-Handler ausgeführt werden, bevor die Aktion Auswahl Logik ausgeführt wird. Also, in Ihrem Fall, wahrscheinlich etwas anderes verursacht das Problem, und ich denke, Sie sollten sich Ihre anderen Nachrichtenhandler, Ihre Registrierungsnummer des Nachrichtenhandlers, usw., da das Problem aufgrund der Tatsache auftritt, dass Ihre Nachrichtenhandler nie ausgeführt wird.

Auch ich denke, Ihre IRouteConstraint Implementierung, GetHttpMethodConstraint, sieht mir verdächtig.

Hier ist mein Registrierungscode für den Nachrichtenhandler:

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

Vielen Dank für Ihre Antwort! Ich bin mir ziemlich sicher, dass 'GetHttpMethodConstraint' korrekt funktioniert, da ich Methoden, die als "PUT" und "DELETE" deklariert sind, erfolgreich mit ihren "nativen" Verben aufrufen kann. Das Problem tritt nur auf, wenn ich mit POST und dem Method-Override-Header aufrufe. Ich habe versucht, den Nachrichtenhandler vor und nach dem Queue-Routen-Mapping zu registrieren, und es hat nicht funktioniert. Ich entfernte auch den anderen Nachrichtenhandler, den ich registriert hatte, um zu sehen, ob es das Problem verursachte, aber kein Glück. Ich kann bestätigen, dass Ihre Anwendung funktioniert, also hat es nichts mit Infrastruktur oder irgendetwas zu tun. – alextercete

+0

@alextercete 'GetHttpMethodConstraint' tritt vor den Nachrichtenhandlern ein. Mein Geld ist darauf, dass Ihr Problem mit "GetHttpMethodConstraint" in Verbindung steht. – tugberk

+0

Ich habe den Code für GetHttpMethodConstraint zur Frage hinzugefügt. Ich denke, dass es nichts Besonderes daran ist ... – alextercete

1

Ich glaube, ich bin mit dem gleichen Problem. Es sieht so aus, als ob die Routenbeschränkungen vor irgendwelchen Nachrichtenhandlern geprüft werden.

Also habe ich eine benutzerdefinierte Einschränkung, die für eine überschriebene HTTP-Methode zu überprüfen, weiß:

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