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.