2011-12-22 2 views
9

Я хочу создать RESTful Json Api для моего приложения MVC3. Мне нужна помощь в обработке нескольких Http-глаголов для управления экземпляром одного объекта.MVC3 RESTful API Routing & Http Verb Handling

Что я прочитал/учился/пробовал атрибуты

MVC (HttpGet, HttpPost и т.д.) позволяют мне иметь контроллер с несколькими действиями, разделяющих то же самое имя, но они по-прежнему должны иметь другой метод подписи.

Ограничения маршрута происходят в модуле маршрутизации до MVC запускает и приводит к тому, что у меня есть 4 явных маршрута и по-прежнему требуются индивидуально названные действия контроллера.

ASP.NET MVC AcceptVerbs and registering routes

Создание пользовательских Http Verb Атрибут может быть использован, чтобы урвать глагол, используемый для доступа к действия, а затем передать его в качестве аргумента в вызове действия - код будет затем обрабатывать случаи коммутации. Проблема с этим подходом заключается в некоторых методах, требующих авторизации, которые должны обрабатываться на уровне фильтра действий, а не внутри самого действия.

http://iwantmymvc.com/rest-service-mvc3


Требования/Цели

  1. Один маршрут подписи для одного объекта экземпляра, MVC, как ожидается, обрабатывать четыре основных HTTP Глаголы: GET, POST, PUT, УДАЛИТЬ.

    context.MapRoute("Api-SingleItem", "items/{id}", 
        new { controller = "Items", action = "Index", id = UrlParameter.Optional } 
    ); 
    
  2. Когда URI не передается параметр Id, действие должен обрабатывать POST и PUT.

    public JsonResult Index(Item item) { return new JsonResult(); } 
    
  3. Когда параметр Id передается URI, одно действие должно обрабатывать GET и DELETE.

    public JsonResult Index(int id) { return new JsonResult(); } 
    

Вопрос

Как я могу иметь больше чем одно действие (общее название и подпись метода), каждый из ответа на уникальный глагол HTTP. Желаемый пример:

[HttpGet] 
public JsonResult Index(int id) { /* _repo.GetItem(id); */} 

[HttpDelete] 
public JsonResult Index(int id) { /* _repo.DeleteItem(id); */ } 

[HttpPost] 
public JsonResult Index(Item item) { /* _repo.addItem(id); */} 

[HttpPut] 
public JsonResult Index(Item item) { /* _repo.updateItem(id); */ } 

ответ

10

Для вызовов RESTful действие не имеет смысла, поскольку вы хотите отличаться только методами HTTP. Таким образом, трюк заключается в использовании имени статического действия, так что разные методы на контроллере отличаются только в методе HTTP, который они принимают.

В то время как MVC framework provides a solution for specifying action names, его можно сделать более кратким и самообучающимся.Мы решили так:

Специальный атрибут используется для указания RESTful методов (это соответствует специальному имя действия):

public sealed class RestfulActionAttribute: ActionNameSelectorAttribute { 
    internal const string RestfulActionName = "<<REST>>"; 

    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo) { 
     return actionName == RestfulActionName; 
    } 
} 

Контроллеры использовать его в сочетании с методом HTTP атрибуты:

public class MyServiceController: Controller { 
    [HttpPost] 
    [RestfulAction] 
    public ActionResult Create(MyEntity entity) { 
     return Json(...); 
    } 

    [HttpDelete] 
    [RestfulAction] 
    public ActionResult Delete(Guid id) { 
     return Json(...); 
    } 

    [HttpGet] 
    [RestfulAction] 
    public ActionResult List() { 
     return Json(...); 
    } 

    [HttpPut] 
    [RestfulAction] 
    public ActionResult Update(MyEntity entity) { 
     return Json(...); 
    } 
} 

И для того, чтобы успешно связывать эти контроллеры, мы используем собственные маршруты с именем статического действия с beforementionned атрибута (который в то же время позволяют также для настройки URL-адреса):

routes.MapRoute(controllerName, pathPrefix+controllerName+"/{id}", new { 
    controller = controllerName, 
    action = RestfulActionAttribute.RestfulActionName, 
    id = UrlParameter.Optional 
}); 

Обратите внимание, что все ваши требования могут быть легко выполнены при таком подходе, насколько я могу судить; вы можете иметь несколько атрибутов [HttpXxx] для одного метода, чтобы один метод принимал несколько HTTP-методов. Сопряженный с некоторым умным (er) ModelBinder, это очень мощно.

+0

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

+0

@ one.beat.consumer: Дело в том, что имя метода * нерелевантно *, потому что все они соответствуют тем же именам действий «<>», которые статически статируются по маршруту. Поэтому вы можете использовать любое количество методов (и успокоительных контроллеров) на одном маршруте, и вы можете иметь любое количество «идентичных» действий (даже если они имеют разные имена методов), которые соответствуют вашему решению A, или вы можете иметь несколько HTTP-адресов методы на метод, если вам нравится, что будет вашим решением B, и вы даже можете смешивать эти решения по своему усмотрению. – Lucero

+0

@ one.beat.consumer: (Исправлено из пространства в предыдущем комментарии) Обратите внимание, что авторизация выполняется на уровне метода, а не на уровне действия, так что это будет работать нормально, если вы добавите атрибут '[Authorize]' некоторые из глагольных методов. – Lucero