MVC3 RESTful API路由& Http动词处理

时间:2011-12-22 22:32:52

标签: asp.net-mvc asp.net-mvc-3 rest asp.net-mvc-routing

我想为我的MVC3应用程序构建一个RESTful Json Api。我需要帮助处理多个Http Verbs来操作单个对象实例。

我读过/研究过的内容

MVC属性(HttpGetHttpPost等)允许我让控制器具有共享相同名称的多个操作,但它们仍然必须具有不同的方法签名。

在 MVC启动之前路由模块中发生路由约束并导致我有4个显式路由,并且仍然需要单独命名的控制器操作。

ASP.NET MVC AcceptVerbs and registering routes

构建自定义Http Verb属性可用于获取用于访问操作的动词,然后在调用操作时将其作为参数传递 - 然后代码将处理切换案例。这种方法的问题是某些方法需要授权,应该在动作过滤器级别处理,而不是在动作本身内部。

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参数时,操作必须处理POSTPUT

    public JsonResult Index(Item item) { return new JsonResult(); }
    
  3. 将Id参数传递给URI时,单个操作应处理GETDELETE

    public JsonResult Index(int id) { return new JsonResult(); }
    
  4. 问题

    如何有多个动作(共享相同的名称和方法签名),每个动作都响应一个唯一的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 个答案:

答案 0 :(得分: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(...);
    }
}

为了成功绑定这些控制器,我们使用带有来自beforementioned属性的静态操作名称的自定义路由(同时也允许自定义URL):

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

请注意,就我所知,这种方法可以轻松满足您的所有要求;您可以在一个方法上具有多个[HttpXxx]属性,以使一个方法接受多个HTTP方法。与一些智能(呃)ModelBinder搭配使用非常强大。