web-dev-qa-db-fra.com

Méthode HttpPost multiple dans le contrôleur API Web

Je commence à utiliser le projet API Web MVC4. J'ai un contrôleur avec plusieurs méthodes HttpPost. Le contrôleur ressemble à ceci:

Manette

public class VTRoutingController : ApiController
{
    [HttpPost]
    public MyResult Route(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }

    [HttpPost]
    public MyResult TSPRoute(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }
}

Here MyRequestTemplate représente la classe de modèle chargée de traiter le fichier Json entrant dans la requête. 

Erreur:

Lorsque je fais une demande en utilisant Fiddler pour http://localhost:52370/api/VTRouting/TSPRoute ou http://localhost:52370/api/VTRouting/Route, une erreur se produit: 

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

Si je supprime une des méthodes ci-dessus, cela fonctionne bien.

Global.asax

J'ai essayé de modifier la table de routage par défaut dans global.asax, mais je reçois toujours l'erreur, je pense que j'ai du mal à définir les itinéraires dans global.asax. Voici ce que je fais dans Global.asax. 

public static void RegisterRoutes(RouteCollection routes)
{
    routes.MapHttpRoute(
        name: "MyTSPRoute",
        routeTemplate: "api/VTRouting/TSPRoute",
        defaults: new { }
    );

    routes.MapHttpRoute(
        name: "MyRoute",
        routeTemplate: "api/VTRouting/Route",
        defaults: new { action="Route" }
    );
}

Je fais la demande dans Fiddler en utilisant POST, en passant json dans RequestBody pour MyRequestTemplate.

118
Habib

Vous pouvez avoir plusieurs actions dans un seul contrôleur.

Pour cela, vous devez faire les deux choses suivantes.

  • Commencez par décorer les actions avec l'attribut ActionName comme 

     [ActionName("route")]
     public class VTRoutingController : ApiController
     {
       [ActionName("route")]
       public MyResult PostRoute(MyRequestTemplate routingRequestTemplate)
       {
         return null;
       }
    
      [ActionName("tspRoute")]
      public MyResult PostTSPRoute(MyRequestTemplate routingRequestTemplate)
      {
         return null;
      }
    }
    
  • Deuxièmement, définissez les itinéraires suivants dans le fichier WebApiConfig.

    // Controller Only
    // To handle routes like `/api/VTRouting`
    config.Routes.MapHttpRoute(
        name: "ControllerOnly",
        routeTemplate: "api/{controller}"               
    );
    
    
    // Controller with ID
    // To handle routes like `/api/VTRouting/1`
    config.Routes.MapHttpRoute(
        name: "ControllerAndId",
        routeTemplate: "api/{controller}/{id}",
        defaults: null,
        constraints: new { id = @"^\d+$" } // Only integers 
    );
    
    // Controllers with Actions
    // To handle routes like `/api/VTRouting/route`
    config.Routes.MapHttpRoute(
        name: "ControllerAndAction",
        routeTemplate: "api/{controller}/{action}"
    );
    
137
Asif Mushtaq

utilisation:

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

ce n'est plus une approche RESTful, mais vous pouvez maintenant appeler vos actions par leur nom (plutôt que de laisser l'API Web en déterminer automatiquement une à partir du verbe) comme ceci:

[POST] /api/VTRouting/TSPRoute

[POST] /api/VTRouting/Route

Contrairement à la croyance populaire, cette approche n’est pas fâcheuse et elle n’abuse pas des API Web. Vous pouvez toujours tirer parti de toutes les fonctionnalités impressionnantes de l'API Web (gestionnaires de délégation, négociation de contenu, mediatypeformatters, etc.). Il vous suffit de laisser tomber l'approche RESTful.

26
Filip W

Une solution bien meilleure à votre problème serait d'utiliser Route qui vous permet de spécifier l'itinéraire sur la méthode par annotation:

[RoutePrefix("api/VTRouting")]
public class VTRoutingController : ApiController
{
    [HttpPost]
    [Route("Route")]
    public MyResult Route(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }

    [HttpPost]
    [Route("TSPRoute")]
    public MyResult TSPRoute(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }
}
25
Wisienkas

Un terminal API Web (contrôleur) est une ressource unique qui accepte les verbes get/post/put/delete. C'est pas un contrôleur MVC normal.

Nécessairement, à /api/VTRouting, il ne peut y avoir que la méthode one HttpPost qui accepte les paramètres que vous envoyez. Le nom de la fonction n'a pas d'importance tant que vous décorez avec le matériel [http]. Je n'ai jamais essayé, cependant.

Edit: Cela ne fonctionne pas. En résolvant, il semble aller par le nombre de paramètres, n'essayant pas de lier le modèle au modèle. 

Vous pouvez surcharger les fonctions pour accepter différents paramètres. Je suis presque certain que vous seriez OK si vous le déclariez comme vous le faites, mais que vous utilisiez des paramètres différents (incompatibles) avec les méthodes. Si les paramètres sont identiques, vous êtes faute de chance en tant que modèle de liaison ne saura pas lequel vous vouliez dire. 

[HttpPost]
public MyResult Route(MyRequestTemplate routingRequestTemplate) {...}

[HttpPost]
public MyResult TSPRoute(MyOtherTemplate routingRequestTemplate) {...}

Cette partie fonctionne

Le modèle par défaut qu'ils donnent lorsque vous en créez un nouveau le rend très explicite, et je dirais que vous devriez vous en tenir à cette convention:

public class ValuesController : ApiController
{
    // GET is overloaded here.  one method takes a param, the other not.
    // GET api/values  
    public IEnumerable<string> Get() { .. return new string[] ... }
    // GET api/values/5
    public string Get(int id) { return "hi there"; }

    // POST api/values (OVERLOADED)
    public void Post(string value) { ... }
    public void Post(string value, string anotherValue) { ... }
    // PUT api/values/5
    public void Put(int id, string value) {}
    // DELETE api/values/5
    public void Delete(int id) {}
}

Si vous voulez créer une classe qui fasse beaucoup de choses, pour une utilisation ajax, il n'y a aucune raison importante de ne pas utiliser un modèle de contrôleur/action standard. La seule différence entre vos méthodes est que les signatures de votre méthode ne sont pas aussi jolies et que vous devez envelopper les éléments dans Json( returnValue) avant de les renvoyer.

Modifier:

La surcharge fonctionne parfaitement lorsque vous utilisez le modèle standard (modifié pour inclure) lorsque vous utilisez des types simples. Je suis aussi allé dans l'autre sens, avec 2 objets personnalisés avec des signatures différentes. Jamais pourrait le faire fonctionner.

  • La liaison avec des objets complexes n'a pas l'air "profonde", c'est donc un non-aller
  • Vous pouvez contourner cela en passant un paramètre supplémentaire, sur la chaîne de requête
  • Un meilleur résumé que ce que je peux donner sur les options disponibles

Cela a fonctionné pour moi dans ce cas, voyez où cela vous mène. Exception pour les tests uniquement.

public class NerdyController : ApiController
{
    public void Post(string type, Obj o) { 
        throw new Exception("Type=" + type + ", o.Name=" + o.Name ); 
    }
}

public class Obj {
    public string Name { get; set; }
    public string Age { get; set; }
}

Et appelé comme ceci la console:

$.post("/api/Nerdy?type=white", { 'Name':'Slim', 'Age':'21' } )
11
Andrew Backer

Il est possible d'ajouter plusieurs méthodes Get et Post dans le même contrôleur API Web. Ici, la route par défaut cause le problème. L'API Web vérifie la correspondance de l'itinéraire du haut vers le bas et, par conséquent, votre itinéraire par défaut correspond à toutes les demandes. Par défaut, une seule méthode Get et Post est possible dans un contrôleur. Placez le code suivant en haut ou commentez l'itinéraire par défaut

    config.Routes.MapHttpRoute("API Default", 
                               "api/{controller}/{action}/{id}",
                               new { id = RouteParameter.Optional });
5
Shahid Ullah

Placez Route Prefix [RoutePrefix ("api/Profiles")] au niveau du contrôleur et mettez une route à la méthode d'action [Route ("LikeProfile")]. Pas besoin de changer quoi que ce soit dans le fichier global.asax

namespace KhandalVipra.Controllers
{
    [RoutePrefix("api/Profiles")]
    public class ProfilesController : ApiController
    {
        // POST: api/Profiles/LikeProfile
        [Authorize]
        [HttpPost]
        [Route("LikeProfile")]
        [ResponseType(typeof(List<Like>))]
        public async Task<IHttpActionResult> LikeProfile()
        {
        }
    }
}
1
Sushil Kumar

Je pense que la question a déjà été répondu. Je recherchais également un contrôleur webApi possédant les mêmes mehtods mais des noms différents. J'essayais d'implémenter la calculatrice en tant que WebApi. La calculatrice a 4 méthodes avec la même signature mais des noms différents. 

public class CalculatorController : ApiController
{
    [HttpGet]
    [ActionName("Add")]
    public string Add(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Add = {0}", num1 + num2);
    }

    [HttpGet]
    [ActionName("Sub")]
    public string Sub(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Subtract result = {0}", num1 - num2);
    }

    [HttpGet]
    [ActionName("Mul")]
    public string Mul(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Multiplication result = {0}", num1 * num2);
    }

    [HttpGet]
    [ActionName("Div")]
    public string Div(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Division result = {0}", num1 / num2);
    }
}

et dans le fichier WebApiConfig que vous avez déjà 

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

Il suffit de définir l’authentification/autorisation sur IIS et vous avez terminé!

J'espère que cela t'aides!

0
Yawar Murtaza

Je viens d'ajouter "action = nom_action" à l'URL et le moteur de routage sait ainsi quelle action je souhaite effectuer. J'ai également ajouté l'attribut ActionName aux actions, mais je ne suis pas sûr que cela soit nécessaire.

0
Rony Tesler