wake-up-neo.com

Mehrere HttpPost-Methode im Web-API-Controller

Ich beginne mit dem MVC4-Web-API-Projekt, ich habe Controller mit mehreren HttpPost-Methoden. Der Controller sieht wie folgt aus:

Controller

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

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

Here MyRequestTemplate steht für die Schablonenklasse, die für die Verarbeitung des Json zuständig ist, der die Anforderung durchläuft. 

Error:

Wenn ich mit Fiddler eine Anfrage für http://localhost:52370/api/VTRouting/TSPRoute oder http://localhost:52370/api/VTRouting/Route stelle, erhalte ich eine Fehlermeldung: 

Es wurden mehrere Aktionen gefunden, die der Anfrage entsprechen

Wenn ich eine der oben genannten Methoden entferne, funktioniert das einwandfrei.

Global.asax

Ich habe versucht, die Standardroutingtabelle in global.asax zu ändern. Ich erhalte jedoch immer noch den Fehler. Ich denke, ich habe Probleme beim Definieren von Routen in global.asax. Folgendes mache ich in 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" }
    );
}

Ich mache die Anfrage in Fiddler mit POST und übergebe json in RequestBody für MyRequestTemplate.

118
Habib

Sie können mehrere Aktionen in einem einzigen Controller ausführen.

Dafür müssen Sie die folgenden zwei Dinge tun.

  • Dekorieren Sie zuerst Aktionen mit ActionName-Attribut wie 

     [ActionName("route")]
     public class VTRoutingController : ApiController
     {
       [ActionName("route")]
       public MyResult PostRoute(MyRequestTemplate routingRequestTemplate)
       {
         return null;
       }
    
      [ActionName("tspRoute")]
      public MyResult PostTSPRoute(MyRequestTemplate routingRequestTemplate)
      {
         return null;
      }
    }
    
  • Zweitens definieren Sie die folgenden Routen in WebApiConfig-Datei.

    // 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

benutzen:

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

es ist kein RESTful-Ansatz mehr, aber Sie können Ihre Aktionen jetzt über den Namen aufrufen (anstatt die Web-API anhand des Verbs automatisch ermitteln zu lassen):

[POST] /api/VTRouting/TSPRoute

[POST] /api/VTRouting/Route

Entgegen der landläufigen Meinung ist an diesem Ansatz nichts falsch, und es wird kein Missbrauch der Web-API gemacht. Sie können immer noch die fantastischen Funktionen der Web-API nutzen (Delegieren von Handlern, Aushandeln von Inhalten, Mediatyp-Formaten usw.).

26
Filip W

Eine viel bessere Lösung für Ihr Problem wäre die Verwendung von Route, mit der Sie die Route der Methode durch Annotation angeben können:

[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

Ein Web-API-Endpunkt (Controller) ist eine einzelne Ressource, die Verben "get/post/put/delete" akzeptiert. Es ist nicht ein normaler MVC-Controller.

Bei /api/VTRouting darf es nur die one HttpPost-Methode geben, die die gesendeten Parameter akzeptiert. Der Funktionsname spielt keine Rolle, solange Sie mit dem [http] -Stil dekorieren. Ich habe es noch nie versucht.

Bearbeiten: Dies funktioniert nicht. Beim Auflösen scheint es die Anzahl der Parameter zu sein, und es wird nicht versucht, an den Typ zu modellieren. 

Sie können die Funktionen überladen, um verschiedene Parameter zu akzeptieren. Ich bin mir ziemlich sicher, dass Sie in Ordnung wären, wenn Sie es so deklarieren würden, aber andere (inkompatible) Parameter für die Methoden verwenden würden. Wenn die Parameter gleich sind, haben Sie kein Glück, da die Modellbindung nicht weiß, welche Sie meinen. 

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

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

Dieser Teil funktioniert

Die Standardvorlage, die sie beim Erstellen einer neuen Vorlage angeben, macht dies ziemlich deutlich, und ich würde sagen, dass Sie sich an diese Konvention halten sollten:

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

Wenn Sie eine Klasse erstellen möchten, die viele Aufgaben für die Verwendung von Ajax erfüllt, gibt es keinen Grund, kein Standardsteuerungs-/Aktionsmuster zu verwenden. Der einzige wirkliche Unterschied ist, dass Ihre Methodensignaturen nicht so hübsch sind, und Sie müssen die Dinge in Json( returnValue) einpacken, bevor Sie sie zurücksenden.

Bearbeiten:

Das Überladen funktioniert gut, wenn Sie die Standardvorlage verwenden (bearbeitet zum Einschließen), wenn Sie einfache Typen verwenden. Ich habe auch den anderen Weg getestet, mit 2 benutzerdefinierten Objekten mit unterschiedlichen Signaturen. Nie konnte es zur Arbeit bringen.

  • Das Binden mit komplexen Objekten sieht nicht "tief" aus, daher ist dies ein Nein
  • Sie könnten dies umgehen, indem Sie einen zusätzlichen Parameter für die Abfragezeichenfolge übergeben
  • Ein besseres Schreiben als ich auf verfügbare Optionen geben kann

Das hat in diesem Fall für mich funktioniert, sehen Sie, wohin es Sie bringt. Ausnahme nur zum Testen.

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

Und nannte diese Form die Konsole:

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

Es ist möglich, mehrere Get- und Post-Methoden in demselben Web-API-Controller hinzuzufügen. Hier ist die Standardroute Ursache für das Problem. Das Web-API prüft, ob die Route von oben nach unten übereinstimmt, und Ihre Standardroute stimmt mit allen Anforderungen überein. Standardmäßig ist nur eine Get- und Post-Methode in einem Controller möglich. Platzieren Sie den folgenden Code entweder oben oder Kommentieren Sie die Standardroute

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

Setzen Sie das Routenpräfix [RoutePrefix ("api/Profiles")] auf Controller-Ebene und setzen Sie eine Route in die Aktionsmethode [Route ("LikeProfile")]. Sie müssen nichts in der Datei global.asax ändern

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

Ich denke, die Frage wurde bereits beantwortet. Ich habe auch nach etwas gesucht, einem webApi-Controller, der die gleichen signierten Modi, aber unterschiedliche Namen hat. Ich habe versucht, den Rechner als WebApi zu implementieren. Der Rechner verfügt über 4 Methoden mit derselben Signatur, aber unterschiedlichen Namen. 

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

und in der WebApiConfig-Datei, die Sie bereits haben 

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

Setzen Sie einfach die Authentifizierung/Autorisierung auf IIS und Sie sind fertig!

Hoffe das hilft!

0
Yawar Murtaza

Ich habe nur "action = action_name" zur URL hinzugefügt und auf diese Weise weiß die Routing-Engine, welche Aktion ich möchte. Ich habe auch das ActionName-Attribut zu den Aktionen hinzugefügt, aber ich bin nicht sicher, ob es benötigt wird.

0
Rony Tesler