Ответ 1
Я извиняюсь заранее, это сообщение немного отходит от того, что вы просили, но все это пузырилось, когда я читал ваш вопрос.
WebAPI Matching Semantic
Согласованная семантика, используемая (маршруты по умолчанию в) WebAPI, довольно проста.
- Он соответствует имени действия с глаголом (verb = GET? искать имя метода, начинающееся с "get" )
- если параметр передан, api ищет действие с параметром
Итак, в примере кода запрос GET без параметра соответствует функции Get*( )
без параметров. A Get содержит и ID ищет Get***(int id)
.
Примеры
Хотя совпадающая семантика проста, она создает некоторую путаницу для разработчиков MVC (ну, по крайней мере, этот разработчик). Давайте рассмотрим несколько примеров:
Нечетные имена - ваш метод get может быть назван чем угодно, если он начинается с "get". Поэтому в случае контроллера виджета вы можете назвать свои функции GetStrawberry()
, и он все равно будет соответствовать. Подумайте о совпадении как-то вроде: methodname.StartsWith("Get")
Несколько методов сопоставления. Что произойдет, если у вас есть два метода Get без параметров? GetStrawberry()
и GetOrange()
. Насколько я могу судить, функция, определенная сначала (верхняя часть файла) в вашем коде, выигрывает... странно. Это имеет побочный эффект, когда некоторые методы в вашем контроллере недоступны (по крайней мере, с маршрутами по умолчанию)... незнакомец.
ПРИМЕЧАНИЕ: бета вела себя так, как указано выше, для "сопоставления нескольких методов" - версия RC и Release немного больше OCD. Он выдает ошибку, если имеется несколько потенциальных совпадений. Это изменение устраняет путаницу нескольких неоднозначных совпадений. В то же время, это уменьшает нашу способность смешивать интерфейсы стиля REST и RPC в одном контроллере, опираясь на порядок и перекрывающиеся маршруты.
Что делать?
Ну, WebAPI является новым, и консенсус по-прежнему объединяется. Сообщество, похоже, довольно много подходит к принципам REST. Тем не менее, не каждый API может или должен быть RESTful, некоторые более естественно выражены в стиле RPC. REST и то, что люди называют REST, похоже, является источником довольно немного of путаница, well по крайней мере до Рой Филдинг.
Как прагматик, я подозреваю, что многие API будут 70% RESTful, с небольшим количеством методов стиля RPC. Во-первых, только распространение контроллера (с учетом метода привязки webapi) будет стимулировать разработчиков bonkers. Во-вторых, WebAPI действительно не имеет встроенного способа создания вложенной структуры путей api (что означает: /api/controller/
легко, но /api/CATEGORY/Sub-Category/Controller
выполнимо, но боль).
С моей точки зрения, мне очень хотелось бы видеть, что структура папок webAPI управляет путями API по умолчанию... что означает, что если я создам папку категории в моем проекте пользовательского интерфейса, тогда /api/Category
будет путь по умолчанию (что-то параллельно этой статье MVC).
Что я сделал?
Итак, у меня было несколько требований: (1) иметь возможность использовать успокоительный синтаксис в большинстве случаев, (2) иметь некоторое "пространство имен" для разделения контроллеров (думаю, подпапки), (3) иметь возможность вызывать дополнительные rpc- когда это необходимо. Реализация этих требований сводилась к умной маршрутизации.
// SEE NOTE AT END ABOUT DataToken change from RC to RTM
Route r;
r = routes.MapHttpRoute( name : "Category1",
routeTemplate : "api/Category1/{controller}/{id}",
defaults : new { id = RouteParameter.Optional } );
r.DataTokens["Namespaces"] = new string[] {" UI.Controllers.Category1"};
r = routes.MapHttpRoute( name : "Category2",
routeTemplate : "api/Category2/{controller}/{id}",
defaults : new { id = RouteParameter.Optional } );
r.DataTokens["Namespaces"] = new string[] {" UI.Controllers.Category2"};
routes.MapHttpRoute( name : "ApiAllowingBL",
routeTemplate : "api/{controller}/{action}/{id}",
defaults : new { id = RouteParameter.Optional } );
routes.MapHttpRoute( name : "DefaultApi",
routeTemplate : "api/{controller}/{id}",
defaults : new { id = RouteParameter.Optional } );
- Первые два маршрута создают маршруты "подпапки". Мне нужно создать маршрут для каждой подпапки, но я ограничился основными категориями, поэтому я только в итоге получаю 3-10 из них. Обратите внимание, как эти маршруты добавляют токен
Namespace
данных, чтобы ограничить поиск классов для определенного маршрута. Это хорошо соответствует типичной настройке пространства имен при добавлении папок в проект пользовательского интерфейса. - Третий маршрут позволяет вызывать имена конкретных методов (например, традиционный mvc). Так как веб-API устраняет имя действия в URL-адресе, относительно легко определить, какие вызовы хотят этот маршрут.
- Последняя запись маршрута - это сетевой маршрут api по умолчанию. Это захватывает любые классы, особенно те, которые находятся за пределами моих "подпапок".
Сказал другой путь
Мое решение дошло до разделения контроллеров немного больше, поэтому /api/XXXX
не переполнялся.
- Я создаю папку в своем проекте пользовательского интерфейса (скажем,
Category1
) и помещаю api-контроллеры в папку. - Visual studio естественно устанавливает пространства имен классов на основе папки. Таким образом,
Widget1
в папкеCategory1
получает пространство имен по умолчаниюUI.Category1.Widget1
. - Естественно, я хотел, чтобы URL-адреса api отображали структуру папок (
/api/Category1/Widget
). Первое сопоставление, которое вы видите выше, обеспечивает, что путем жесткого кодирования/api/Category1
в маршрут, токенNamespace
ограничивает классы, которые будут искать соответствующий контроллер.
ПРИМЕЧАНИЕ: по умолчанию релиз
DataTokens
по умолчанию равен null. я не если это ошибка или функция. Поэтому я написал немного помощника метод и добавлен в мой файлRouteConfig.cs
....
r.AddRouteToken("Namespaces", new string[] {"UI.Controllers.Category1"});
private static Route AddRouteToken(this Route r, string key, string[] values) {
//change from RC to RTM ...datatokens is null
if (r.DataTokens == null) {
r.DataTokens = new RouteValueDictionary();
}
r.DataTokens[key] = values;
return r;
}
ПРИМЕЧАНИЕ 2: даже подумал, что это сообщение в WebAPI 1, так как @Jamie_Ide указывает на комментарии, указанное выше решение не работает в WebAPI 2, потому что
IHttpRoute.DataTokens
не имеет сеттера. Чтобы обойти это, вы можете использовать простой метод расширения следующим образом:
private static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name, string routeTemplate, object defaults, object constraints, string[] namespaceTokens)
{
HttpRouteValueDictionary defaultsDictionary = new HttpRouteValueDictionary(defaults);
HttpRouteValueDictionary constraintsDictionary = new HttpRouteValueDictionary(constraints);
IDictionary<string, object> tokens = new Dictionary<string, object>();
tokens.Add("Namespaces", namespaceTokens);
IHttpRoute route = routes.CreateRoute(routeTemplate, defaultsDictionary, constraintsDictionary, dataTokens: tokens, handler:null);
routes.Add(name, route);
return route;
}