Маршрутизація веб-API - api / {controller} / {action} / {id} «дисфункції» api / {controller} / {id}


94

У мене є маршрут за замовчуванням у Global.asax:

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

Я хотів мати змогу націлити певну функцію, тому створив інший маршрут:

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

Отже, у своєму контролері я маю:

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

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

Дзвінок .../api/records/bycategoryid/5дасть мені те, що я хочу. Однак дзвінок .../api/records/1дасть мені помилку

Знайдено кілька дій, які відповідають запиту: ...

Я розумію, чому саме так - маршрути просто визначають, які URL-адреси є дійсними, але коли справа стосується збігу функцій, Get(int id)і ByCategoryId(int id)збігаються api/{controller}/{id}, і саме це бентежить структуру.

Що мені потрібно зробити, щоб маршрут API за замовчуванням знову запрацював, і зберегти той, з яким {action}? Я думав створити інший контролер з іменем, RecordByCategoryIdControllerщо відповідає маршруту API за замовчуванням, про який я б запитував .../api/recordbycategoryid/5. Однак я вважаю, що це "брудне" (отже, незадовільне) рішення. Я шукав відповіді на це, і жоден навчальний посібник з використання маршруту {action}навіть не згадує цю проблему.

Відповіді:


104

Механізм маршруту використовує ту ж послідовність, що і ви додаєте в нього правила. Отримавши перше відповідне правило, він припинить перевірку інших правил і здійснить це для пошуку контролера та дії.

Отже, вам слід:

  1. Поставте свої конкретні правила попереду загальних правил (наприклад, за замовчуванням), що означає RouteTable.Routes.MapHttpRouteспочатку використати для відображення "WithActionApi", а потім "DefaultApi".

  2. Видаліть defaults: new { id = System.Web.Http.RouteParameter.Optional }параметр правила "WithActionApi", оскільки, коли ідентифікатор не є обов’язковим, URL-адреса типу "/ api / {part1} / {part2}" ніколи не потрапляє в "DefaultApi".

  3. Додайте іменовану дію до вашого "DefaultApi", щоб повідомити механізму маршруту, яку дію ввести. Інакше, як тільки у вас буде кілька дій у вашому контролері, движок не буде знати, яку з них використовувати, і видасть "Знайдено кілька дій, які відповідають запиту: ...". Потім, щоб він відповідав вашому методу Get, використовуйте ActionNameAttribute .

Тож ваш маршрут повинен сподобатися:

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

І ваш контролер:

[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
Я спробував наведену вище пораду, і все працює як слід. Щиро дякую за цей "секрет".
Мікаель Карузо

У пункті 2 у вашій відповіді, якщо idце необов’язково, URL-адреса, як-от, /api/{part1}/{part2}все одно може перейти в DefaultApiмаршрут, якщо для WithActionApiмаршруту не знайдено жодної дії . Будь ласка, виправте мене, якщо я помиляюся.
орад

що трапиться, якщо я хочу мати лише api / {controller} / {action}. без ідентифікатора Якщо хтось інший стикається з такою ж проблемою. забути про WebApiConfig. читайте про маршрутизацію атрибутів.
ahsant

40

Ви можете вирішити свою проблему за допомогою маршрутизації атрибутів

Контролер

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

URI в jquery

api/category/1

Конфігурація маршруту

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

і Ваша маршрутизація за замовчуванням працює як маршрутизація за замовчуванням на основі домовленостей

Контролер

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

URI в Jquery

/api/records/1 

Конфігурація маршруту

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

Ознайомтеся зі статтею для отримання додаткової інформації Маршрутизація атрибутів та маршрутизація на основі задумів тут та це


відповідь богу. але чи не можна додати для всіх api / {controller} / {action} / {id} разом з api / {controller} / {id}?
karim

Маршрутизація атрибутів остаточно вирішує проблему. Один важливий момент: До веб-API 2 шаблони проектів Web API генерували такий код: protected void Application_Start () {WebApiConfig.Register (GlobalConfiguration.Configuration); } Якщо маршрутизація атрибутів увімкнена, цей код видасть виняток. Якщо ви оновлюєте існуючий проект веб-API для використання маршрутизації атрибутів, переконайтеся, що оновили цей код конфігурації таким чином: protected void Application_Start () {GlobalConfiguration.Configure (WebApiConfig.Register); }
Deeb

0

Спробуйте це.

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

Можливою причиною може бути також те, що ви не успадкували контролер від ApiController. Що трапилося зі мною, потрібно було деякий час, щоб зрозуміти те саме.


0

Щоб розрізнити маршрути, спробуйте додати обмеження, яке має бути числовим:

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 }
         );  
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.