14

Ist es möglich, GETs und POSTs in separate API Controller-Typen zu trennen und mit demselben Routenpräfix darauf zuzugreifen?Mehrere Controllertypen mit demselben Routenpräfix ASP.NET Web Api

Hier sind meine Controller:

[RoutePrefix("api/Books")] 
public class BooksWriteController : EventStoreApiController 
{ 
    [Route("")] 
    public void Post([FromBody] CommandWrapper commandWrapper){...} 
} 

[RoutePrefix("api/Books")] 
public class BooksReadController : MongoDbApiController 
{ 
    [Route("")] 
    public Book[] Get() {...} 


    [Route("{id:int}")] 
    public Book Get(int id) {...} 
} 

Antwort

29

Die Web-API (1.x-2.x) unterstützt nicht mehrere Attributrouten mit demselben Pfad auf verschiedenen Controllern. Das Ergebnis ist ein 404, da die gesamte Route mit mehr als einem Controller übereinstimmt und die Web-API zu diesem Zeitpunkt das Ergebnis als mehrdeutig ansieht.

Beachten Sie, dass MVC Core unterstützt dieses Szenario Hinweis: MVC Core dient als beide MVC & Web-API.

Wenn Sie Web API 2.11 (oder neuer) verwenden, können Sie eine Routenbeschränkung für die HTTP-Methode pro Controller erstellen und sie anstelle des integrierten Routenattributs verwenden. Das folgende Beispiel zeigt, dass Sie RoutePrefix oder direkt Routen (wie kmacdonalds Antwort) verwenden können.

using System.Collections.Generic; 
using System.Net.Http; 
using System.Web.Http; 
using System.Web.Http.Routing; 

public class BooksWriteController : ApiController 
{ 
    [PostRoute("api/Books")] 
    public void Post() { } 
} 

[RoutePrefix("api/books")] 
public class BooksReadController : ApiController 
{ 
    [GetRoute] 
    public void Get() { } 

    [GetRoute("{id:int}")] 
    public void Get(int id) { } 
} 

Diese beiden Klassen vereinfachen die Verwendung des Attributs Zwangsroute

class GetRouteAttribute : MethodConstraintedRouteAttribute 
{ 
    public GetRouteAttribute(string template) : base(template ?? "", HttpMethod.Get) { } 
} 

class PostRouteAttribute : MethodConstraintedRouteAttribute 
{ 
    public PostRouteAttribute(string template) : base(template ?? "", HttpMethod.Post) { } 
} 

Diese Klasse

class MethodConstraintedRouteAttribute : RouteFactoryAttribute 
{ 
    public MethodConstraintedRouteAttribute(string template, HttpMethod method) 
     : base(template) 
    { 
     Method = method; 
    } 

    public HttpMethod Method 
    { 
     get; 
     private set; 
    } 

    public override IDictionary<string, object> Constraints 
    { 
     get 
     { 
      var constraints = new HttpRouteValueDictionary(); 
      constraints.Add("method", new MethodConstraint(Method)); 
      return constraints; 
     } 
    } 
} 

Dies ist nur eine Standardroute Einschränkung, nit Zwänge auf die Route erzeugt ermöglicht das Hinzufügen : Möglicherweise möchten Sie das Constraints-Objekt zwischenspeichern, um Zuweisungen zu reduzieren.

class MethodConstraint : IHttpRouteConstraint 
{ 
    public HttpMethod Method { get; private set; } 

    public MethodConstraint(HttpMethod method) 
    { 
     Method = method; 
    } 

    public bool Match(HttpRequestMessage request, 
         IHttpRoute route, 
         string parameterName, 
         IDictionary<string, object> values, 
         HttpRouteDirection routeDirection) 
    { 
     return request.Method == Method; 
    } 
} 
+0

Dies funktioniert perfekt. – user1145404

+1

@YishaiGalatzer: Aus irgendeinem Grund brauchen Sie hier den benutzerdefinierten 'MethodConstraint' anstatt [System.Web.Http.Routing.HttpMethodConstraint] (https://msdn.microsoft.com/en-us/library/system.web. http.routing.httpmethodconstraint (v = vs.118) .aspx)? – Serguei

+1

Der Punkt beschränkt die Route selbst. Die integrierte Eigenschaft wirkt nur in der Aktionsauswahl, die zu spät ausgeführt wird. –

3

Sie nicht immer eine RoutePrefix auf dem Controller angeben müssen. direkt auf den Web-Methoden setzen Sie könnten nur die Strecke:

public class BooksWriteController : EventStoreApiController 
{ 
    [Route("api/Books")] 
    public void Post([FromBody] CommandWrapper commandWrapper){...} 
} 

public class BooksReadController : MongoDbApiController 
{ 
    [Route("api/Books")] 
    public TaskTypeInfo[] Get() {...} 


    [Route("api/Books/{id:int}")] 
    public TaskTypeInfo Get(int id) {...} 
} 

Allerdings könnte ich mir vorstellen, dass Ihr RoutePrefix auf beiden Controllern funktionieren würde. Ich denke, das Attribut RoutePrefix wird in Verbindung mit dem Route Attribut verwendet, das die Route tatsächlich definiert. Das heißt, solange Sie keine widersprüchlichen Routen haben (das ist ein großes Problem), sollten Sie in Ordnung sein.

+2

Danke, das ist nicht das Problem. Das Problem ist die Auswahl des richtigen Controllertyps basierend auf GET oder POST – user1145404