Web-API-Routing - API / {Controller} / {Aktion} / {ID} "Funktionsstörungen" API / {Controller} / {ID}

94

Ich habe die Standardroute in Global.asax:

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

Ich wollte in der Lage sein, auf eine bestimmte Funktion abzuzielen, also habe ich eine andere Route erstellt:

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

In meinem Controller habe ich also:

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

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

Anrufen .../api/records/bycategoryid/5gibt mir, was ich will. Ein Anruf .../api/records/1gibt mir jedoch den Fehler

Es wurden mehrere Aktionen gefunden, die der Anforderung entsprechen: ...

Ich verstehe, warum das so ist - die Routen definieren nur, welche URLs gültig sind, aber wenn es um Funktionsabgleich geht, sowohl um Get(int id)als auch um ByCategoryId(int id)Übereinstimmung api/{controller}/{id}, was das Framework verwirrt .

Was muss ich tun, damit die Standard-API-Route wieder funktioniert und die beibehalten wird {action}? Ich dachte daran, einen anderen Controller RecordByCategoryIdControllerzu erstellen , der der Standard-API-Route entspricht, für die ich eine Anfrage stellen würde .../api/recordbycategoryid/5. Ich finde das jedoch eine "schmutzige" (also unbefriedigende) Lösung. Ich habe nach Antworten zu diesem Thema gesucht und es gibt kein Tutorial zur Verwendung einer Route {action}, in dem dieses Problem überhaupt erwähnt wird.

Mickael Caruso
quelle

Antworten:

104

Die Routen-Engine verwendet dieselbe Reihenfolge, in der Sie Regeln hinzufügen. Sobald die erste übereinstimmende Regel angezeigt wird, werden andere Regeln nicht mehr überprüft, und es wird nach Regeln und Aktionen gesucht.

Deshalb solltest du:

  1. RouteTable.Routes.MapHttpRouteStellen Sie Ihre spezifischen Regeln vor Ihre allgemeinen Regeln (wie die Standardregeln). Dies bedeutet, dass Sie zuerst "WithActionApi" und dann "DefaultApi" zuordnen.

  2. Entfernen Sie den defaults: new { id = System.Web.Http.RouteParameter.Optional }Parameter Ihrer "WithActionApi" -Regel, da eine URL wie "/ api / {part1} / {part2}" niemals in "DefaultApi" eingeht, sobald die ID optional ist.

  3. Fügen Sie Ihrem "DefaultApi" eine benannte Aktion hinzu, um der Routen-Engine mitzuteilen, welche Aktion eingegeben werden soll. Andernfalls weiß die Engine nicht, welche Aktion verwendet werden soll, sobald Sie mehr als eine Aktion in Ihrem Controller haben, und gibt "Es wurden mehrere Aktionen gefunden, die der Anforderung entsprechen: ..." aus. Verwenden Sie dann ein ActionNameAttribute , damit es mit Ihrer Get-Methode übereinstimmt .

Ihre Route sollte also folgendermaßen aussehen:

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

Und dein Controller:

[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" };
}
Chris Li
quelle
1
Ich habe den obigen Rat ausprobiert und alles funktioniert wie erwartet. Vielen Dank für dieses "Geheimnis".
Mickael Caruso
Wenn Punkt 2 in Ihrer Antwort idoptional ist, wird die URL wie /api/{part1}/{part2}möglicherweise immer noch in die DefaultApiRoute verschoben, wenn für die WithActionApiRoute keine übereinstimmende Aktion gefunden wird . Bitte korrigieren Sie mich, wenn ich falsch liege.
Orad
Was passiert, wenn ich nur API / {Controller} / {Aktion} haben möchte? ohne id. Wenn jemand anderes vor dem gleichen Problem steht. Vergessen Sie WebApiConfig. Lesen Sie mehr über Attribut-Routing.
Ahsant
40

Sie können Ihr Problem mithilfe des Attribut-Routings lösen

Regler

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

URI in jquery

api/category/1

Routenkonfiguration

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

und Ihr Standardrouting funktioniert als standardmäßiges konventionelles Routing

Regler

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

URI in Jquery

/api/records/1 

Routenkonfiguration

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

Weitere Informationen finden Sie im Artikel. Attribut-Routing und onvention-basiertes Routing hier und hier

MSTdev
quelle
Gott antworte. Aber können wir nicht für alle hinzufügen, api / {controller} / {action} / {id} zusammen mit api / {controller} / {id}?
Karim
Das Attribut-Routing löst das Problem definitiv. Ein wichtiger Punkt: Vor Web API 2 haben die Web API-Projektvorlagen Code wie folgt generiert: protected void Application_Start () {WebApiConfig.Register (GlobalConfiguration.Configuration); } Wenn das Attribut-Routing aktiviert ist, löst dieser Code eine Ausnahme aus. Wenn Sie ein vorhandenes Web-API-Projekt aktualisieren, um Attribut-Routing zu verwenden, müssen Sie diesen Konfigurationscode wie folgt aktualisieren: protected void Application_Start () {GlobalConfiguration.Configure (WebApiConfig.Register); }
Deeb
0

Versuche dies.

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 }

        );
    }
}
Amit
quelle
0

Der mögliche Grund kann auch sein, dass Sie Controller nicht von ApiController geerbt haben. Es passierte eine Weile, bis ich das Gleiche verstanden hatte.

Paritosh Tiwari
quelle
0

Fügen Sie zur Unterscheidung der Routen eine Einschränkung hinzu, deren ID numerisch sein muss:

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 }
         );  
Derek Wade
quelle