95 votes

Acheminement des API Web - api/{controller}/{action}/{id} "dysfonctions" api/{controller}/{id}

J'ai la route par défaut dans Global.asax :

 RouteTable.Routes.MapHttpRoute(
         name: "DefaultApi",
         routeTemplate: "api/{controller}/{id}",
         defaults: new { id = System.Web.Http.RouteParameter.Optional }
         );

Je voulais pouvoir cibler une fonction spécifique, j'ai donc créé une autre route :

RouteTable.Routes.MapHttpRoute(
         name: "WithActionApi",
         routeTemplate: "api/{controller}/{action}/{id}",
         defaults: new { id = System.Web.Http.RouteParameter.Optional }
         );

Donc, dans mon contrôleur, j'ai :

    public string Get(int id)
    {
        return "object of id id";
    }        

    [HttpGet]
    public IEnumerable<string> ByCategoryId(int id)
    {
        return new string[] { "byCategory1", "byCategory2" };
    }

Appel à .../api/records/bycategoryid/5 me donnera ce que je veux. Cependant, appeler .../api/records/1 me donnera l'erreur

Plusieurs actions ont été trouvées qui correspondent à la demande : ...

Je comprends pourquoi - les routes définissent simplement quelles URL sont valides, mais lorsqu'il s'agit de faire correspondre des fonctions, les deux types d'URL ne sont pas valides. Get(int id) y ByCategoryId(int id) match api/{controller}/{id} qui est ce que confond le cadre.

Que dois-je faire pour que la route API par défaut fonctionne à nouveau, et garder celle avec {action} ? J'ai pensé à créer un contrôleur différent nommé RecordByCategoryIdController pour correspondre à la route API par défaut, pour laquelle je demanderais .../api/recordbycategoryid/5 . Cependant, je trouve que c'est une solution "sale" (donc insatisfaisante). J'ai cherché des réponses à ce sujet et je n'ai trouvé aucun tutoriel sur l'utilisation d'une route avec le protocole {action} ne mentionne même pas cette question.

105voto

Chris Li Points 2236

Le moteur d'itinéraire utilise la même séquence au fur et à mesure que vous y ajoutez des règles. Dès qu'il obtient la première règle correspondante, il arrête de vérifier les autres règles et prend celle-ci pour rechercher le contrôleur et l'action.

Donc, vous devriez :

  1. Placez vos règles spécifiques avant vos règles générales (comme les règles par défaut), ce qui signifie que vous devez utiliser les règles suivantes RouteTable.Routes.MapHttpRoute pour mettre en correspondance "WithActionApi" d'abord, puis "DefaultApi".

  2. Retirer le defaults: new { id = System.Web.Http.RouteParameter.Optional } de votre règle "WithActionApi" car une fois que l'id est optionnel, une url comme "/api/{part1}/{part2}" n'ira jamais dans "DefaultApi".

  3. Ajoutez une action nommée à votre "DefaultApi" pour indiquer au moteur de route l'action à saisir. Sinon, si vous avez plus d'une action dans votre contrôleur, le moteur ne saura pas laquelle utiliser et lancera "Multiple actions were found that match the request : ...". Ensuite, pour qu'il corresponde à votre méthode Get, utilisez une balise Attribut ActionName .

Votre itinéraire devrait donc ressembler à ceci :

// Map this rule first
RouteTable.Routes.MapRoute(
     "WithActionApi",
     "api/{controller}/{action}/{id}"
 );

RouteTable.Routes.MapRoute(
    "DefaultApi",
    "api/{controller}/{id}",
    new { action="DefaultAction", id = System.Web.Http.RouteParameter.Optional }
);

Et votre contrôleur :

[ActionName("DefaultAction")] //Map Action and you can name your method with any text
public string Get(int id)
{
    return "object of id id";
}        

[HttpGet]
public IEnumerable<string> ByCategoryId(int id)
{
    return new string[] { "byCategory1", "byCategory2" };
}

1 votes

J'ai essayé les conseils ci-dessus et tout fonctionne comme prévu. Merci beaucoup pour ce "secret".

0 votes

Au point 2 de votre réponse, si id est facultative, alors une URL comme /api/{part1}/{part2} peut encore entrer dans DefaultApi si aucune action correspondante n'est trouvée pour WithActionApi route. Veuillez me corriger si je me trompe.

0 votes

Que se passe-t-il si je veux avoir api/{controller}/{action} seulement. sans id. Si quelqu'un d'autre est confronté au même problème, oubliez WebApiConfig et lisez ce qui concerne le routage des attributs.

40voto

MSTdev Points 664

Vous pouvez résoudre votre problème avec l'aide de Routage par attributs

Contrôleur

[Route("api/category/{categoryId}")]
public IEnumerable<Order> GetCategoryId(int categoryId) { ... }

URI dans jquery

api/category/1

Configuration des routes

using System.Web.Http;

namespace WebApplication
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API routes
            config.MapHttpAttributeRoutes();

            // Other Web API configuration not shown.
        }
    }
}

et votre routage par défaut fonctionne par défaut routage par convention

Contrôleur

public string Get(int id)
    {
        return "object of id id";
    }   

URI dans Jquery

/api/records/1 

Configuration des routes

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Attribute routing.
        config.MapHttpAttributeRoutes();

        // Convention-based routing.
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

Consultez l'article pour plus d'informations Routage par attributs et routage par intervention ici & este

0 votes

Mais ne pouvons-nous pas ajouter pour tous, api/{controller}/{action}/{id} avec api/{controller}/{id} ?

0 votes

Le routage par attributs résout définitivement le problème. Un point important : Avant l'API Web 2, les modèles de projet API Web généraient un code comme celui-ci : protected void Application_Start() { WebApiConfig.Register(GlobalConfiguration.Configuration) ; }Si le routage d'attributs est activé, ce code lèvera une exception. Si vous mettez à niveau un projet d'API Web existant pour utiliser le routage par attributs, veillez à mettre à jour ce code de configuration comme suit : protected void Application_Start() { GlobalConfiguration.Configure(WebApiConfig.Register) ; }.

0voto

Amit Points 1

Essayez ça.

public class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services

        var json = config.Formatters.JsonFormatter;
        json.SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
        config.Formatters.Remove(config.Formatters.XmlFormatter);

        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional , Action =RouteParameter.Optional }

        );
    }
}

0voto

La raison possible est que vous n'avez pas hérité de Controller de ApiController. Cela m'est arrivé et il m'a fallu un certain temps pour comprendre ce problème.

0voto

Derek Wade Points 437

Pour différencier les routes, essayez d'ajouter une contrainte selon laquelle l'id doit être numérique :

RouteTable.Routes.MapHttpRoute(
         name: "DefaultApi",
         routeTemplate: "api/{controller}/{id}",
         constraints: new { id = @"\d+" }, // Only matches if "id" is one or more digits.
         defaults: new { id = System.Web.Http.RouteParameter.Optional }
         );

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X