2013-01-21 42 views
8

如果URL中沒有提供任何操作,似乎Web API有一個內置的默認邏輯來使用HTTP Verb作爲操作名稱。例如,我有這樣的路線:如何修改框架的默認Web API路由?

 config.Routes.MapHttpRoute(
      name: "DefaultApiController", 
      routeTemplate: "api/{controller}" 
     ); 

這裏是我的操作:

public IEnumerable<Conference> Get() 
    { 
     ... 
    } 

    [ActionName("current")] 
    public IEnumerable<Conference> GetCurrent() 
    { 
     ... 
    } 

當我去〜/會議用GET動詞,它會帶你到「獲取()「動作。如果使用POST動詞,它會帶您到「Post([FromBody] Conference value)」動作...等等。有衝突,雖然,當你嘗試到〜/會議/ GetCurrent(即使我有[ActionName(「電流」)]在上面):沒有發現

多個動作相匹配的要求,即: 系統.Collections.Generic.IEnumerable 1[MyApp.Models.Conference] Get() on type MyApp.Api.ConferencesController System.Collections.Generic.IEnumerable 1 [MyApp.Models.Conference] GetCurrent()上型MyApp.Api.ConferencesController

這意味着框架使用StartsWith代替等於確定的默認動作。當匹配動詞與動作時,它也忽略了ActionName屬性。

我的問題是如何使框架的默認動作完全匹配動詞,而不是使用StartsWith邏輯?一個GET動詞只應該匹配一個Get()動作,而不是Get(),GetCurrent()GetPast()等(特別是當它忽略ActionName屬性時)。

編輯 爲了簡單起見,我只顯示了上面的一條路線。如果我顯示所有仍在草稿中的路線,我認爲這可能會有所幫助。我試圖讓一個完全工作的REST API,同時還留出空間添加自己的自定義操作:

public static void Register(HttpConfiguration config) 
    { 
     config.Routes.MapHttpRoute(
      name: "DefaultApiControllerActionId", 
      routeTemplate: "api/{controller}/{action}/{id}", 
      defaults: null, 
      constraints: new { action = @"^[a-zA-Z]+$", id = @"^\d+$" } // action must start with character 
     ); 

     config.Routes.MapHttpRoute(
      name: "DefaultApiControllerActionName", 
      routeTemplate: "api/{controller}/{action}/{name}", 
      defaults: null, 
      constraints: new { action = @"^[a-zA-Z]+$", name = @"^[a-zA-Z]+$" } // action and name must start with character 
     ); 

     config.Routes.MapHttpRoute(
      name: "DefaultApiControllerId", 
      routeTemplate: "api/{controller}/{id}", 
      defaults: null, 
      constraints: new { id = @"^\d+$" } // id must be all digits 
     ); 

     config.Routes.MapHttpRoute(
      name: "DefaultApiControllerAction", 
      routeTemplate: "api/{controller}/{action}", 
      defaults: null, 
      constraints: new { action = @"^[a-zA-Z]+$" } // action must start with character 
     ); 

     config.Routes.MapHttpRoute(
      name: "DefaultApiController", 
      routeTemplate: "api/{controller}" 
     ); 

UPDATE 看來,增加HTTP動詞約束上幫助:

 config.Routes.MapHttpRoute(
      name: "DefaultApiControllerGet", 
      routeTemplate: "api/{controller}", 
      defaults: new { action = "Get" }, 
      constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) } 
     ); 

     config.Routes.MapHttpRoute(
      name: "DefaultApiControllerPost", 
      routeTemplate: "api/{controller}", 
      defaults: new { action = "Post" }, 
      constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Post) } 
     ); 

     config.Routes.MapHttpRoute(
      name: "DefaultApiControllerPut", 
      routeTemplate: "api/{controller}", 
      defaults: new { action = "Put" }, 
      constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Put) } 
     ); 

     config.Routes.MapHttpRoute(
      name: "DefaultApiControllerDelete", 
      routeTemplate: "api/{controller}", 
      defaults: new { action = "Delete" }, 
      constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Delete) } 
     ); 

回答

7

編輯:既然你對這個問題做了一個很大的編輯,我需要改變回應:

總之 - 這將永遠不會與網絡開箱API,因爲默認情況下將派遣行動:

    基於動作名稱如果{action}是路由數據基於
  1. 對HTTP動詞

然而,這兩種方法的一部分

  • 不能在單個控制器中混合使用,因此您將無法使用單個控制器(這是您正在嘗試執行的操作)的兩種方法調度操作。

    您有三種方式來解決這個問題:

    1. 返工你的資源,讓你有行動名調度和基於動詞的disptaching獨立的人(這是很不理想)

    2. 爲每個嵌套路由手動註冊路由。這樣你可以通過HTTP動詞進行調度,但路由清楚地指向特定的動作。您可以使用類似AttributeRouting(https://github.com/mccalltd/AttributeRouting)來簡化此操作。不利的一面是,你最終得到了 - 有效 - 每個動作一個路由

    3. 實現一個新的IActionSelector,這將允許您在單個控制器中混用基於動詞的和基於動作名稱的調度。這是最「低級」的解決方案,但看起來完全像你想做的事情。我上週公佈的演練 - http://www.strathweb.com/2013/01/magical-web-api-action-selector-http-verb-and-action-name-dispatching-in-a-single-controller/

  • +0

    對於REST API,我仍然需要得到〜/會議仍儘管 – Basem

    +0

    編輯工作:OK,現在你發佈的所有路由,畫面更清晰;) –

    +0

    真棒文章thx!另外AttributeRouting聽起來很有希望,但似乎Web API性能和緩存功能沒有被利用。 – Basem