Подтвердить что ты не робот

Как метод в MVC WebApi сопоставляется с http-глаголом?

В 5-минутном видео по следующей ссылке, на отметке 1:10, Джон Галлоуэй говорит, что добавление метода, называемого DeleteComment, в его класс контроллера ControlController, будет автоматически адаптироваться к удалению http-глагола.

Как MVC с WebApi знает, как разбить методы на правильные глаголы? Я знаю, что маршрутизация в файле global.asax.cs направляет запросы правильному контроллеру, но как запрос на удаление "сопоставляется по соглашению" с методом удаления или любым методом? Особенно, когда может быть более одного метода для каждого глагола? "По соглашению" заставляет меня думать, что он просто смотрит на первое слово в имени метода... но если это так, ему нужно будет прочитать сигнатуру методов, чтобы сказать два метода удаления или два метода разделить друг от друга... и где все это определено?

Видео: http://www.asp.net/web-api/videos/getting-started/delete-and-update

Спасибо!

Изменить: Вот код в классе ValuesController, который входит в шаблон WebApi. Это было источником моего первоначального вопроса. Как работает "конвенция", которая различает эти (и любые другие методы в контроллере)?

// GET /api/values
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }

    // GET /api/values/5
    public string Get(int id)
    {
        return value;
    }
4b9b3361

Ответ 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;
}

Ответ 2

Это происходит довольно часто. И есть разные взгляды на это. На данный момент я лично не подписался на какую-либо конкретную идею, но, похоже, один из них с одним контроллером на ресурс является самым популярным среди сообщества REST.

Итак, в основном вы можете:

  • Действие Expose в вашем маршруте (веб-API обрабатывает слово action, подобное MVC), но это, как правило, не предназначено для использования.
  • Определить методы с различными параметрами в соответствии с этим post
  • Как я уже сказал, наиболее рекомендуется использовать один контроллер на ресурс. Таким образом, даже в примере Web API контроллер для сбора объекта отличается от контроллера для самой сущности. Прочитайте это сообщение от Роба Конри и here - это Гленн ответ.