2012-05-03 2 views
29

В 5-минутном видео по следующей ссылке, с отметкой 1:10, Джон Галлоуэй говорит, что добавление метода, называемого DeleteComment, в его класс контроллера ControlController, автоматически будет сопоставляться картам с удаленным http-глаголом.Как метод в MVC WebApi сопоставляется с 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; 
    } 
+3

В WebAPI я _think_ вы должны иметь контроллер на ресурс, чтобы вы не имели бы два УДАЛИТЬ методы. –

+1

Спасибо, Шейн, это хорошая информация, но я имею в виду, в частности, как метод одиночной записи и метод с несколькими записями. GET, вероятно, является более подходящим примером, но у вас может быть метод, который получает одну запись по идентификатору, а другой - все записи с определенным FK. Но они оба используют глагол GET; Я не понимаю, где существует разметка маршрутизации, которая различается между ними. –

ответ

58

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

WebAPI Matching Semantic
согласующего семантический, используемый по умолчанию ( маршрутов на в) WebAPI довольно просто.

  1. Это совпадает с именем действия с глаголом (глагол = GET? Ищет имя методы, начиная с «получить»)
  2. , если параметр передается, то апи ищет действие с параметром

Таким образом, в вашем примере кода запрос GET без параметра соответствует функции Get*() без параметров. A Get содержит и ID ищет Get***(int id).

Примеры
Хотя соответствующий семантический просто, это создает некоторую путаницу для разработчиков MVC (ну, по крайней мере, этот разработчик). Давайте рассмотрим несколько примеров:

Odd Names - Ваш метод получения может быть назван чем угодно, если он начинается с «get». Поэтому в случае контроллера виджета вы можете назвать свои функции GetStrawberry(), и он все равно будет соответствовать. Подумайте о совпадении как-то вроде: methodname.StartsWith("Get")

Несколько методов сопоставления - Что произойдет, если у вас есть два метода Get без параметров? GetStrawberry() и GetOrange(). Насколько я могу судить, функция, определенная сначала (верхняя часть файла) в вашем коде, выигрывает ... странно. Это имеет побочный эффект, когда некоторые методы в вашем контроллере недоступны (по крайней мере, с маршрутами по умолчанию) ... незнакомец.

ПРИМЕЧАНИЕ: бета вели себя, как описано выше для «совпадающих нескольких методов» - версия Release RC & является немного более OCD. Он выдает ошибку, если имеется несколько потенциальных совпадений. Это изменение устраняет путаницу нескольких неоднозначных совпадений. В то же время, это уменьшает нашу способность смешивать интерфейсы стиля REST и RPC в одном контроллере, опираясь на перекрывающиеся маршруты &.

Что делать?
Ну, WebAPI является новым, и консенсус по-прежнему коалесцирует. Сообщество, похоже, довольно много подходит к принципам REST. Тем не менее, не каждый API может или должен быть RESTful, некоторые более естественно выражены в стиле RPC. REST &, что люди называют REST, как представляется, является источником quite a bitof confusion, wellat least toRoy Fielding.

Как прагматик, я подозреваю, что многие API будут 70% RESTful, с небольшим количеством методов стиля RPC. Во-первых, только распространение контроллера (с учетом метода привязки webapi) будет стимулировать разработчиков bonkers. Во-вторых, WebAPI действительно не имеет встроенного способа создания вложенной структуры путей api (что означает: /api/controller/ легко, но /api/CATEGORY/Sub-Category/Controller выполнимо, но боль).

С моей точки зрения, я бы хотел, чтобы структура папок webAPI управляла путями API по умолчанию ... что означает, что если я создам папку категории в моем проекте пользовательского интерфейса, тогда /api/Category будет по умолчанию (что-то parallel to this MVC article).

Что я сделал?
Итак, у меня было несколько требований: (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 являются недействительными по умолчанию. Я не уверен, что this is a bug, or a feature. Поэтому я написал небольшой вспомогательный метод и добавил к моему 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; 
} 
+1

Спасибо, EBarr, это очень полезно. Ваше решение в конце немного над моей головой, но первая часть определенно ответила на мой вопрос, и я думаю, что последняя часть будет чрезвычайно полезна для повторного посещения после того, как я узнаю немного больше.Я подозревал, что было какое-то сравнение между именем метода в вашем классе контроллера и глаголами http, но пока кто-то не подтвердил это и не сказал мне, как это работает, я действительно не верил своим собственным подозрениям. Благодаря! –

+0

@AndrewBSchultz - рада помочь. ..posting немного лучшего решения. – EBarr

+0

Спасибо @EBarr! Очень полезно. –

3

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

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

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

Спасибо Aliostad хорошие статьи и ответ - но одно наблюдение: Я специально спрашиваю об этом (это входит в пример кода int he ValuesController class в проекте шаблона WebApi). Я не думаю, что это то, о чем говорит Гленн, потому что это все в одном контроллере: Я забыл, что эти комментарии плохо форматируются. Я добавил код, который я хотел бы вставить здесь, в мой оригинальный пост. –

Смежные вопросы