Routing API Web: api / {controller} / {action} / {id} "dysfunctions" api / {controller} / {id}


94

Ho il percorso predefinito in Global.asax:

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

Volevo essere in grado di indirizzare una funzione specifica, quindi ho creato un altro percorso:

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

Quindi, nel mio controller, ho:

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

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

Chiamare .../api/records/bycategoryid/5mi darà quello che voglio. Tuttavia, la chiamata .../api/records/1mi darà l'errore

Sono state trovate più azioni che corrispondono alla richiesta: ...

Capisco perché questo è: le rotte definiscono solo quali URL sono validi, ma quando si tratta di corrispondenza delle funzioni, sia Get(int id)e ByCategoryId(int id)corrispondenza api/{controller}/{id}, che è ciò che confonde il framework.

Cosa devo fare per far funzionare nuovamente il percorso API predefinito e mantenerlo con {action}? Ho pensato di creare un controller diverso denominato RecordByCategoryIdControllerper abbinare il percorso API predefinito, per il quale avrei richiesto .../api/recordbycategoryid/5. Tuttavia, trovo che sia una soluzione "sporca" (quindi insoddisfacente). Ho cercato risposte su questo e nessun tutorial là fuori sull'uso di un percorso {action}menziona anche questo problema.

Risposte:


104

Il motore di rotte utilizza la stessa sequenza in cui aggiungi le regole. Una volta ottenuta la prima regola abbinata, smetterà di controllare le altre regole e la utilizzerà per cercare controller e azione.

Quindi dovresti:

  1. Metti le tue regole specifiche prima delle regole generali (come default), il che significa usare RouteTable.Routes.MapHttpRouteper mappare prima "WithActionApi", poi "DefaultApi".

  2. Rimuovi il defaults: new { id = System.Web.Http.RouteParameter.Optional }parametro della regola "WithActionApi" perché una volta che l'id è facoltativo, l'URL come "/ api / {part1} / {part2}" non andrà mai in "DefaultApi".

  3. Aggiungi un'azione con nome a "DefaultApi" per indicare al motore di rotte quale azione inserire. Altrimenti, una volta che hai più di una azione nel tuo controller, il motore non saprà quale usare e lancia "Sono state trovate più azioni che corrispondono alla richiesta: ...". Quindi per fare in modo che corrisponda al tuo metodo Get, usa un ActionNameAttribute .

Quindi il tuo percorso dovrebbe essere questo:

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

E il tuo 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" };
}

1
Ho provato il consiglio sopra e tutto funziona come previsto. Grazie mille per quel "segreto".
— Mickael Caruso

Al punto 2 della tua risposta, se idè facoltativo, l'URL come /api/{part1}/{part2}potrebbe ancora andare nel DefaultApipercorso se non viene trovata alcuna azione corrispondente per il WithActionApipercorso. Per favore correggimi se sbaglio.
— orad

cosa succede se desidero avere solo api / {controller} / {action}. senza id. Se qualcun altro sta affrontando lo stesso problema. dimentica WebApiConfig. leggi informazioni sul routing degli attributi.
— ahsant

40

Puoi risolvere il tuo problema con l'aiuto del routing degli attributi

Controller

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

URI in jquery

api/category/1

Configurazione del percorso

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

e il tuo instradamento predefinito funziona come instradamento basato su convenzione predefinito

Controller

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

URI in Jquery

/api/records/1 

Configurazione del percorso

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

Consulta l'articolo per ulteriori informazioni Instradamento degli attributi e instradamento basato sull'invenzione qui e questo


Dio rispondi. ma non possiamo aggiungere per tutti, api / {controller} / {action} / {id} insieme a api / {controller} / {id}?
— karim

Il routing degli attributi risolve definitivamente il problema. Un punto importante: prima dell'API Web 2, i modelli di progetto API Web generavano codice come questo: protected void Application_Start () {WebApiConfig.Register (GlobalConfiguration.Configuration); } Se il routing degli attributi è abilitato, questo codice genererà un'eccezione. Se aggiorni un progetto API Web esistente per utilizzare il routing degli attributi, assicurati di aggiornare questo codice di configurazione come segue: protected void Application_Start () {GlobalConfiguration.Configure (WebApiConfig.Register); }
— Deeb

0

Prova questo.

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 }

        );
    }
}

0

Il motivo possibile può anche essere che non hai ereditato il controller da ApiController. È successo con me che ci è voluto un po 'per capire lo stesso.


0

Per differenziare i percorsi, prova ad aggiungere un vincolo che l'id deve essere numerico:

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 }
         );  
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.