MVC3 RESTful API маршрутизация и обработка глагола Http - PullRequest
9 голосов
/ 23 декабря 2011

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

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

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

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

ASP.NET MVC AcceptVerbs и регистрация маршрутов

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

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


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

  1. Одна подпись маршрута для одного объекта экземпляра, MVC, как ожидается, будет обрабатывать четыре основных глагола Http: GET, POST, PUT, DELETE.

    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); */ }

1 Ответ

10 голосов
/ 23 декабря 2011

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

Хотя инфраструктура MVC предоставляет решение для указания имен действий , ее можно сделать более лаконичной и понятной. Мы решили это так:

Специальный атрибут используется для указания методов 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(...);
    }
}

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

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

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

...