web-dev-qa-db-fra.com

Plusieurs types de contrôleurs avec le même préfixe de route ASP.NET Web Api

Est-il possible de séparer les GET et les POST en différents types de contrôleurs API et d'y accéder en utilisant le même préfixe de route?

Voici mes contrôleurs:

[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) {...}
}
21
user1145404

L'API Web (1.x-2.x) ne prend pas en charge plusieurs itinéraires d'attributs avec le même chemin sur différents contrôleurs. Le résultat est un 404, car toute la route correspond à plusieurs contrôleurs et à ce stade, l'API Web considérera le résultat comme ambigu.

Notez que MVC Core prend en charge cette note de scénario: MVC Core sert à la fois de MVC et d'API Web.

Si vous choisissez d'utiliser l'API Web 2.11 (ou une version plus récente), vous pouvez créer une contrainte de route pour la méthode http par contrôleur et l'utiliser à la place de l'attribut de route intégré. L'exemple ci-dessous montre que vous pouvez utiliser RoutePrefix ou directement Routes (comme la réponse de kmacdonald).

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

Ces deux classes simplifient l'utilisation de l'attribut route de contrainte

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

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

Cette classe permet d'ajouter des contraintes à la route générée

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

C'est juste une contrainte de route standard, nit: vous pouvez vouloir mettre en cache l'objet de contraintes pour réduire les allocations.

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;
    }
}
35
Yishai Galatzer

Vous n'avez pas toujours besoin de spécifier un RoutePrefix sur votre contrôleur. vous pouvez simplement mettre l'itinéraire directement sur les méthodes web:

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

Cependant, j'imagine que votre RoutePrefix fonctionnerait bien sur les deux contrôleurs. Je pense que l'attribut RoutePrefix est utilisé en conjonction avec l'attribut Route qui définit réellement l'itinéraire. Cela signifie que tant que vous n'avez pas d'itinéraires conflictuels (c'est un gros problème), ça devrait aller.

3
kmacdonald