web-dev-qa-db-fra.com

Routage et héritage d'attributs

Je joue avec l'idée d'avoir un contrôleur de base qui utilise un référentiel générique pour fournir les méthodes CRUD de base pour mes contrôleurs API afin de ne pas avoir à dupliquer le même code de base dans chaque nouveau contrôleur. Mais je rencontre des problèmes avec l'attribut de routage reconnu lorsqu'il se trouve dans le contrôleur de base. Pour montrer exactement quel est le problème que j'ai, j'ai créé un contrôleur WebAPI vraiment simple.

Lorsque j'ai une méthode Get dans le contrôleur principal et qu'elle hérite directement de ApiController, je n'ai aucun problème et cela fonctionne comme prévu.

[RoutePrefix("admin/test")]
public class TestController : ApiController
{
    [Route("{id:int:min(1)}")]
    public string Get(int id)
    {
        return "Success";
    }
}

Lorsque je déplace la méthode Get dans un contrôleur de base, elle renvoie le contenu de la page 404.

[RoutePrefix("admin/test")]
public class TestController : TestBaseController
{

}

public class TestBaseController : ApiController
{
    [Route("{id:int:min(1)}")]
    public string Get(int id)
    {
        return "Success";
    }
}

Quelques notes plus intéressantes:

  • Je peux accéder à l'action à GET/Test/1. Il le trouve donc toujours sur la base de la route par défaut.

  • Lorsque j'essaie d'accéder à POST/admin/test, il retourne le JSON suivant

    {"Message": "Aucune ressource HTTP correspondant à l'URI de demande ' http://test.com/admin/test '.", "MessageDetail": "Aucun type correspondant à la requête n'a été trouvée. le contrôleur nommé "admin". " }

Quelqu'un connaît-il un moyen de faire fonctionner le routage avec les attributs d'un contrôleur de base?

63
Jeff Treuting

Les routes d'attribut ne peuvent pas être héritées. Il s'agissait d'une décision de conception délibérée. Nous ne nous sentions pas bien et nous n'avons pas vu de scénarios valides où il serait logique de les hériter.

Pourriez-vous donner un scénario plus réaliste quant à l'endroit où vous souhaitez utiliser cela?

[ Mise à jour (24/03/2014)]
Dans la prochaine version 5.2 de MVC Web API, il y aura un point d'extensibilité appelé System.Web.Http.Routing.IDirectRouteProvider à travers lequel vous pouvez activer le scénario d'héritage que vous recherchez ici. Vous pouvez essayer cela vous-même en utilisant les dernières versions de nuit (la documentation sur la façon d'utiliser les versions de nuit est ici )

[ Mise à jour (31/07/2014)]
Exemple de réalisation de cette opération dans l'API Web 2.2 Libération:

config.MapHttpAttributeRoutes(new CustomDirectRouteProvider());

//---------

public class CustomDirectRouteProvider : DefaultDirectRouteProvider
{
    protected override IReadOnlyList<IDirectRouteFactory> 
    GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
    {
        // inherit route attributes decorated on base class controller's actions
        return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>
        (inherit: true);
    }
}
77
Kiran Challa

En utilisant Web API 2.2, vous pouvez:

public class BaseController : ApiController
{
    [Route("{id:int}")]
    public string Get(int id)
    {
        return "Success:" + id;
    }
}
[RoutePrefix("api/values")]
public class ValuesController : BaseController
{
}

config.MapHttpAttributeRoutes(new CustomDirectRouteProvider());
public class CustomDirectRouteProvider : DefaultDirectRouteProvider
{
    protected override IReadOnlyList<IDirectRouteFactory> 
    GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
    {
        return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>
        (inherit: true);
    }
}

comme indiqué ici: http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-22

25
Dejan

Je l'ai.

[Route("api/baseuploader/{action}")]
public abstract class BaseUploaderController : ApiController
{
    [HttpGet] 
    public string UploadFile() 
    {
        return "UploadFile";
    }
}


[Route("api/values/{action}")]
public class ValuesController : BaseUploaderController
{
    [HttpGet]
    public string Get(int id)
    {
        return "value";
    }
}

Une mise en garde ici est que le paramètre d'action d'itinéraire doit être le même que le nom de l'action. Je ne pouvais pas trouver un moyen de contourner cela. (Vous ne pouvez pas renommer l'itinéraire avec un RouteAttribute)

3
parliament