Маршрутизация веб-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 }
);