Plusieurs actions ont été trouvées qui correspondent à la demande dans Web Api

243

Je reçois toujours cette erreur lorsque j'essaie d'avoir 2 méthodes "Get"

Plusieurs actions correspondant à la demande ont été trouvées: webapi

J'ai regardé autour des autres questions similaires à ce sujet sur la pile, mais je ne comprends pas.

J'ai 2 noms différents et j'utilise l'attribut "HttpGet"

[HttpGet]
public HttpResponseMessage Summary(MyVm vm)
{
    return null;
}

[HttpGet]
public HttpResponseMessage FullDetails()
{
    return null;
}
chobo2
la source

Réponses:

485

Votre plan de route ressemble probablement à ceci:

routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });

Mais pour avoir plusieurs actions avec la même méthode http, vous devez fournir à webapi plus d'informations via l'itinéraire comme suit:

routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional });

Notez que le routeTemplate inclut désormais une action. Beaucoup plus d'informations ici: http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api

Mettre à jour:

Bon, maintenant que je pense que je comprends ce que vous cherchez, voici une autre prise de vue à ce sujet:

Vous n'avez peut-être pas besoin du paramètre action url et devez décrire le contenu que vous recherchez d'une autre manière. Puisque vous dites que les méthodes retournent des données de la même entité, laissez simplement les paramètres faire la description pour vous.

Par exemple, vos deux méthodes pourraient être transformées en:

public HttpResponseMessage Get()
{
    return null;
}

public HttpResponseMessage Get(MyVm vm)
{
    return null;
}

Quel type de données transmettez-vous dans l'objet MyVm? Si vous pouvez simplement passer des variables via l'URI, je vous suggère de suivre cette voie. Sinon, vous devrez envoyer l'objet dans le corps de la demande et ce n'est pas très HTTP de vous lorsque vous faites un GET (cela fonctionne cependant, utilisez simplement [FromBody] en face de MyVm).

J'espère que cela illustre que vous pouvez avoir plusieurs méthodes GET dans un seul contrôleur sans utiliser le nom de l'action ou même l'attribut [HttpGet].

Jed
la source
Y a-t-il des avantages à faire d'une manière ou d'une autre? Si je fais le secondaire, dois-je simplement mettre l'action Http sur chaque méthode? Est-ce le gros inconvénient?
chobo2
3
Que l'un détienne un avantage sur l'autre dépend vraiment de votre projet. Si vous construisez une api RESTful, vous voudrez utiliser les conventions HTTP (GET, POST, PUT, DELETE ...). Dans ce cas, le premier bloc de code de routage est le chemin à parcourir, mais vous voudrez un contrôleur différent pour chaque entité que vous exposez via l'API. En fonction de vos noms de méthode, je suppose que ce n'est pas le cas, utilisez donc le routage plus descriptif. Lorsque votre itinéraire comprend l'action, vous souhaiterez mettre explicitement l'attribut http sur chaque méthode.
Jed
1
@ chobo2 Pourquoi ne pas simplement utiliser des méthodes nommées en conséquence dans le contrôleur? GetSummary (MyVm wm) et GetDetails ()
Jed
1
Merci pour votre réponse, cela m'a juste aidé à comprendre pourquoi la résolution de l'itinéraire ne fonctionnait pas même si mes deux actions avaient des noms différents. Je suis vraiment confus quant à la raison pour laquelle ce n'est pas seulement le comportement par défaut (c'est-à-dire pourquoi le modèle de route par défaut dans webapiconfig.cs n'inclut pas "{action}")!
Tejas Sharma
1
@bruno si vous utilisez des zones, vous pouvez également ajouter des API spécifiques "admin" comme celle-ci dans AdminAreaRegistration stackoverflow.com/a/9849011/16940
Simon_Weaver
67

Mise à jour à partir de l'API Web 2.

Avec cette configuration d'API dans votre fichier WebApiConfig.cs:

public static void Register(HttpConfiguration config)
{
    //// Web API routes
    config.MapHttpAttributeRoutes(); //Don't miss this

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

Vous pouvez acheminer notre contrôleur comme ceci:

[Route("api/ControllerName/Summary")]
[HttpGet]
public HttpResponseMessage Summary(MyVm vm)
{
    rturn null;
}

[Route("api/ControllerName/FullDetails")]
[HttpGet]
public HttpResponseMessage FullDetails()
{
    return null;
}

Où ControllerName est le nom de votre contrôleur (sans "contrôleur"). Cela vous permettra d'obtenir chaque action avec l'itinéraire détaillé ci-dessus.

Pour plus d'informations: http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2

Marc Stevenson
la source
J'ai vraiment aimé cette solution. Mon itinéraire par défaut reste le même et j'ai un itinéraire "d'exception" pour les exceptions
Leandro De Mello Fagundes
vous pouvez également mapper les paramètres dans l'url EX: [Route ("api / ControllerName / Summary / {vm}")]
nulltron
15

Dans l'API Web (par défaut), les méthodes sont choisies en fonction d'une combinaison de méthodes HTTP et de valeurs de route .

MyVmressemble à un objet complexe, lu par le formateur du corps, vous avez donc deux méthodes identiques en termes de données de route (car aucune d'entre elles n'a de paramètres de la route) - ce qui rend impossible pour le répartiteur ( IHttpActionSelector) de correspondre à la bonne .

Vous devez les différencier par chaîne de requête ou par paramètre de route pour résoudre l'ambiguïté.

Filip W
la source
14

Après de nombreuses recherches sur le Web et la recherche du formulaire de routage le plus approprié si vous avez trouvé ce qui suit

config.Routes.MapHttpRoute("DefaultApiWithId", "Api/{controller}/{id}", new { id =RouteParameter.Optional }, new { id = @"\d+" });
config.Routes.MapHttpRoute("DefaultApiWithAction", "Api/{controller}/{action}");

Ces mappages s'appliquent à la fois au mappage des noms d'actions et à la convention http de base (GET, POST, PUT, DELETE)

Moatasem bakri
la source
9
Pour moi, cela a fonctionné, mais seulement après avoir changé l'ordre des itinéraires dans la configuration de l'itinéraire afin que celui qui a une action apparaisse en premier
Fredrik Stolpe
exactement l' ordre est important ici
AT
5

Sans utiliser d'actions, les options seraient:

  1. déplacez l'une des méthodes vers un autre contrôleur, afin qu'elles ne s'affrontent pas.

  2. utilisez une seule méthode qui prend le paramètre, et si elle est nulle, appelez l'autre méthode à partir de votre code.

Joanna Derks
la source
Ce pourrait être une solution, mais pas optimale, de toute façon +1 de mon côté :)
satish kumar V
4

Cette solution a fonctionné pour moi.

Veuillez placer Route2 en premier dans WebApiConfig. Ajoutez également HttpGet et HttpPost avant chaque méthode et incluez le nom du contrôleur et le nom de la méthode dans l'url.

WebApiConfig =>

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

Contrôleur =>

public class ValuesController : ApiController
{

    [HttpPost]
    public string GetCustomer([FromBody] RequestModel req)
    {
        return "Customer";
    }

    [HttpPost]
    public string GetCustomerList([FromBody] RequestModel req)
    {
        return "Customer List";
    }
}

URL =>

http://localhost:7050/api/Values/GetCustomer

http://localhost:7050/api/Values/GetCustomerList
Alan Rezende
la source
4

C'est la réponse pour tous ceux qui savent que tout est correct et qui a vérifié 50 fois .....

Assurez-vous que vous ne regardez pas à plusieurs reprises RouteConfig.cs.

Le fichier que vous souhaitez modifier est nommé WebApiConfig.cs

En outre, cela devrait probablement ressembler exactement à ceci:

using System.Web.Http;

namespace My.Epic.Website
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
          config.MapHttpAttributeRoutes();

          // api/Country/WithStates
          config.Routes.MapHttpRoute(
            name: "ControllerAndActionOnly",
            routeTemplate: "api/{controller}/{action}",
            defaults: new { },
            constraints: new { action = @"^[a-zA-Z]+([\s][a-zA-Z]+)*$" });

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

J'aurais pu me sauver environ 3 heures.

CarComp
la source
1
Merci, vous m'avez sauvé environ 3 heures
geedubb
3

J'ai trouvé que lorsque j'ai deux méthodes Get, une sans paramètre et une avec un type complexe comme paramètre, j'ai eu la même erreur. J'ai résolu ce problème en ajoutant un paramètre factice de type int, nommé Id, comme premier paramètre, suivi de mon paramètre de type complexe. J'ai ensuite ajouté le paramètre de type complexe au modèle d'itinéraire. Ce qui suit a fonctionné pour moi.

Obtenez d'abord:

public IEnumerable<SearchItem> Get()
{
...
}

Deuxième get:

public IEnumerable<SearchItem> Get(int id, [FromUri] List<string> layers)
{
...
}

WebApiConfig:

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}/{layers}",
    defaults: new { id = RouteParameter.Optional, layers RouteParameter.Optional }
);
Andrew Terwiel
la source
3

Cela est possible en raison de l'utilisation du contrôleur MVC au lieu du contrôleur API Web. Vérifiez l'espace de noms dans le contrôleur Web API, il devrait être comme suit

using System.Net;
using System.Net.Http;
using System.Web.Http;

Si l'espace de noms est le suivant, il s'agit de l'erreur ci-dessus dans l'appel de la méthode du contrôleur web api

using System.Web;
using System.Web.Mvc;
Ujwal Khairnar
la source
2

Veuillez vérifier que vous disposez de deux méthodes qui ont un nom différent et les mêmes paramètres.

Si tel est le cas, supprimez l'une des méthodes et essayez.

Ramesh
la source
2

Je suis tombé sur ce problème en essayant d'augmenter mes contrôleurs WebAPI avec des actions supplémentaires.

Supposons que vous auriez

public IEnumerable<string> Get()
{
    return this.Repository.GetAll();
}

[HttpGet]
public void ReSeed()
{
    // Your custom action here
}

Il existe maintenant deux méthodes qui satisfont la demande de / api / controller qui déclenche le problème décrit par TS.

Je ne voulais pas ajouter de paramètres "factices" à mes actions supplémentaires, j'ai donc examiné les actions par défaut et j'ai trouvé:

[ActionName("builtin")]
public IEnumerable<string> Get()
{
    return this.Repository.GetAll();
}

pour la première méthode en combinaison avec la liaison de route "double":

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { action = "builtin", id = RouteParameter.Optional },
    constraints: new { id = @"\d+" });

config.Routes.MapHttpRoute(
    name: "CustomActionApi",
    routeTemplate: "api/{controller}/{action}");

Notez que même s'il n'y a pas de paramètre "action" dans le premier modèle de route, vous pouvez toujours configurer une action par défaut nous permettant de séparer le routage des appels WebAPI "normaux" et les appels à l'action supplémentaire.

Bjørn van Dommelen
la source
2

Dans mon cas, tout allait bien

1) Web Config a été configuré correctement 2) Le préfixe de route et les attributs de route étaient corrects

J'obtenais toujours l'erreur. Dans mon cas, l'attribut "Route" (en appuyant sur F12) pointait vers System.Web.MVc mais pas System.Web.Http qui a causé le problème.

Hemanth Vatti
la source
Cette réponse m'a beaucoup aidé!
tvb108108
1

Vous pouvez ajouter [Route("api/[controller]/[action]")]à votre classe de contrôleur.

[Route("api/[controller]/[action]")]
[ApiController]
public class MySuperController : ControllerBase
{
 ...
}
emert117
la source
0

Je sais que c'est une vieille question, mais parfois, lorsque vous utilisez des ressources de service comme AngularJS pour vous connecter à WebAPI, assurez-vous que vous utilisez la bonne route, sinon cette erreur se produit.

Suresh Kaushik
la source
0

Assurez-vous de ne PAS décorer vos méthodes de contrôleur pour les actions par défaut GET | PUT | POST | DELETE avec l'attribut [HttpPost / Put / Get / Delete]. J'avais ajouté cet attribut à mon action de contrôleur Vanilla Post et cela a provoqué un 404.

J'espère que cela aide quelqu'un, car cela peut être très frustrant et interrompre les progrès.

Entrez
la source
0

Par exemple => TestController

        [HttpGet]
        public string TestMethod(int arg0)
        {
            return "";
        }

        [HttpGet]
        public string TestMethod2(string arg0)
        {
            return "";
        }

        [HttpGet]
        public string TestMethod3(int arg0,string arg1)
        {
            return "";
        }

Si vous ne pouvez modifier que le fichier WebApiConfig.cs.

 config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/",
                defaults: null
            );

C'est tout :)

Et résultat: entrez la description de l'image ici

Cemre Onur Baş
la source
0

Avez-vous essayé comme:

[HttpGet("Summary")]
public HttpResponseMessage Summary(MyVm vm)
{
    return null;
}

[HttpGet("FullDetails")]
public HttpResponseMessage FullDetails()
{
    return null;
}
Deepak Shaw
la source
1
Cela ne sera pas compilé dans les projets non .NET Core, car l' HttpGetattribut n'a pas de constructeur qui accepte un argument de chaîne.
Hoppeduppeanut