Routing internetowego interfejsu API - api / {controller} / {action} / {id} „dysfunctions” api / {controller} / {id}

94

Mam domyślną trasę w Global.asax:

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

Chciałem mieć możliwość kierowania na określoną funkcję, więc stworzyłem inną trasę:

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

Tak więc w moim kontrolerze mam:

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

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

Dzwonienie .../api/records/bycategoryid/5da mi to, czego chcę. Jednak dzwonienie .../api/records/1da mi błąd

Znaleziono wiele działań pasujących do żądania: ...

Rozumiem, dlaczego tak jest - trasy po prostu definiują, które adresy URL są prawidłowe, ale jeśli chodzi o dopasowywanie funkcji, zarówno, jak Get(int id)i ByCategoryId(int id)dopasuj api/{controller}/{id}, co myli strukturę.

Co muszę zrobić, aby domyślna trasa interfejsu API znów działała i zachować tę z {action}? Pomyślałem o stworzeniu innego kontrolera o nazwie RecordByCategoryIdControllerpasującej do domyślnej trasy API, o którą chciałbym poprosić .../api/recordbycategoryid/5. Uważam jednak, że jest to „brudne” (a więc niezadowalające) rozwiązanie. Szukałem odpowiedzi na ten temat i nie ma tam samouczka na temat korzystania z trasy, {action}nawet jeśli wspomina o tym problemie.

Mickael Caruso
źródło

Odpowiedzi:

105

Silnik tras używa tej samej kolejności, w jakiej dodajesz do niego reguły. Gdy otrzyma pierwszą dopasowaną regułę, przestanie sprawdzać inne reguły i podejmie to w celu wyszukania kontrolera i akcji.

Więc powinieneś:

  1. Określ swoje reguły przed regułami ogólnymi (takimi jak domyślne), co oznacza, RouteTable.Routes.MapHttpRouteże najpierw użyj opcji „WithActionApi”, a następnie „DefaultApi”.

  2. Usuń defaults: new { id = System.Web.Http.RouteParameter.Optional }parametr reguły „WithActionApi”, ponieważ gdy identyfikator jest opcjonalny, adres URL, taki jak „/ api / {part1} / {part2}”, nigdy nie przejdzie do „DefaultApi”.

  3. Dodaj nazwaną akcję do swojego „DefaultApi”, aby poinformować silnik trasy, którą akcję należy wprowadzić. W przeciwnym razie, gdy masz więcej niż jedną akcję w kontrolerze, silnik nie będzie wiedział, której z nich użyć i wyświetli komunikat „Znaleziono wiele akcji, które pasują do żądania: ...”. Następnie, aby pasował do metody Get, użyj ActionNameAttribute .

Twoja trasa powinna wyglądać następująco:

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

Twój kontroler:

[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
źródło
1
Wypróbowałem powyższe rady i wszystko działa zgodnie z oczekiwaniami. Bardzo dziękuję za ten „sekret”.
Mickael Caruso
W punkcie 2 w Twojej odpowiedzi, jeśli idjest opcjonalne, wówczas adres URL /api/{part1}/{part2}może nadal przejść do DefaultApitrasy, jeśli nie zostanie znalezione pasujące działanie dla WithActionApitrasy. Proszę popraw mnie jeżeli się mylę.
orad
co się stanie, jeśli chcę mieć tylko interfejs API / {controller} / {action}. bez identyfikatora. Jeśli ktoś inny ma ten sam problem. zapomnij o WebApiConfig. przeczytaj o routingu atrybutów.
ahsant
40

Możesz rozwiązać swój problem za pomocą routingu atrybutów

Kontroler

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

URI w jquery

api/category/1

Konfiguracja trasy

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

a Twój domyślny routing działa jako domyślny routing oparty na konwencji

Kontroler

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

URI w Jquery

/api/records/1 

Konfiguracja trasy

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

Przejrzyj artykuł, aby uzyskać więcej informacji o routingu atrybutów i routingu opartym na konwencji, tutaj i to

MSTdev
źródło
Boże odpowiedz. ale czy nie możemy dodać dla wszystkich api / {controller} / {action} / {id} wraz z api / {controller} / {id}?
karim
Routing atrybutów definitywnie rozwiązuje problem. Jedna ważna kwestia: przed Web API 2 szablony projektów Web API generowały następujący kod: protected void Application_Start () {WebApiConfig.Register (GlobalConfiguration.Configuration); } Jeśli routing atrybutów jest włączony, ten kod zgłosi wyjątek. W przypadku uaktualnienia istniejącego projektu interfejsu API sieci Web do korzystania z routingu atrybutów należy zaktualizować ten kod konfiguracji do następującego kodu: protected void Application_Start () {GlobalConfiguration.Configure (WebApiConfig.Register); }
Deeb
0

Spróbuj tego.

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
źródło
0

Możliwym powodem może być również to, że nie odziedziczyłeś kontrolera po ApiController. Zdarzyło mi się trochę czasu, zanim zrozumiałem to samo.

Paritosh Tiwari
źródło
0

Aby rozróżnić trasy, spróbuj dodać ograniczenie, że identyfikator musi być numeryczny:

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
źródło