Маршрутизация веб-API - api/{controller}/{действие}/{id} "dysfunctions" api/{controller}/{id}

У меня есть Маршрут по умолчанию в 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} даже не упоминает об этой проблеме.

Ответ 1

Механизм маршрута использует ту же последовательность, что и в нее добавляются правила. Как только он получит первое совпадающее правило, он перестанет проверять другие правила и возьмет это для поиска контроллера и действий.

Итак, вы должны:

  • Поместите свои специальные правила перед вашими общими правилами (например, по умолчанию), что означает использование RouteTable.Routes.MapHttpRoute для сопоставления "WithActionApi" сначала, а затем "DefaultApi" .

  • Удалите параметр defaults: new { id = System.Web.Http.RouteParameter.Optional } вашего правила "WithActionApi", поскольку после того, как идентификатор является необязательным, url как "/api/{part1}/{part2}" никогда не войдет в "DefaultApi" .

  • Добавьте именованное действие в ваш "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" };
}

Ответ 2

Вы можете решить свою проблему с помощью Маршрутизация атрибутов

контроллер

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

Просмотрите статью для дополнительной информации Маршрутизация атрибутов и маршрутизация на основе данных здесь и это

Ответ 3

Попробуйте это.

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 }

        );
    }
}

Ответ 4

Возможной причиной также может быть то, что вы не унаследовали Controller от ApiController. Случилось со мной, потребовалось время, чтобы понять то же самое.

Ответ 5

Чтобы разграничить маршруты, попробуйте добавить ограничение, в котором идентификатор должен быть числовым:

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