Asp.net-Mvc

MVC3 RESTful API 路由和 Http 動詞處理

  • March 29, 2013

我想為我的 MVC3 應用程序建構一個 RESTful Json Api。我需要幫助來處理多個 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 }
);
  1. 當 URI 未傳遞 Id 參數時,操作必須處理POSTPUT
public JsonResult Index(Item item) { return new JsonResult(); }
  1. 當將 Id 參數傳遞給 URI 時,單個操作應處理GETDELETE
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); */ }

對於 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 配合使用,這是非常強大的。

引用自:https://stackoverflow.com/questions/8610451