2012-11-28 54 views
84

我在Global.asax中的默認路由:的Web API路由 - API/{控制器}/{行動} /(編號) 「功能障礙」 API/{控制器}/{ID}

RouteTable.Routes.MapHttpRoute(
     name: "DefaultApi", 
     routeTemplate: "api/{controller}/{id}", 
     defaults: new { id = System.Web.Http.RouteParameter.Optional } 
     ); 

我希望能夠針對特定的功能,所以我創建了另一條路:

RouteTable.Routes.MapHttpRoute(
     name: "WithActionApi", 
     routeTemplate: "api/{controller}/{action}/{id}", 
     defaults: new { id = System.Web.Http.RouteParameter.Optional } 
     ); 

所以,在我的控制,我有:

public string Get(int id) 
    { 
     return "object of id id"; 
    }   

    [HttpGet] 
    public IEnumerable<string> ByCategoryId(int id) 
    { 
     return new string[] { "byCategory1", "byCategory2" }; 
    } 

調用.../api/records/bycategoryid/5會給我我想要的。 但是,調用.../api/records/1會給我的錯誤

多個動作中發現匹配的要求,即:......

我明白這是爲什麼 - 路由只是定義哪些URL是有效的,但當涉及到的功能匹配,既Get(int id)ByCategoryId(int id)比賽api/{controller}/{id},這是什麼混淆的框架。

什麼我需要做的就是默認的API路線重新工作,並保持一個與{action}?我想創建一個名爲RecordByCategoryIdController的不同控制器來匹配默認的API路由,爲此我會請求.../api/recordbycategoryid/5。但是,我發現這是一個「骯髒」(因此不令人滿意)的解決方案。我已經找到了這個答案,並沒有教程在那裏使用{action}甚至提到這個問題的路線。

回答

95

路由引擎使用同樣的順序添加的規則進去。一旦獲得第一個匹配的規則,它將停止檢查其他規則,並採取此操作來搜索控制器和操作。

所以,你應該:

  1. 提前把你的一般規則的具體規則(如默認值),這意味着使用RouteTable.Routes.MapHttpRoute映射「WithActionApi」,然後再「DefaultApi」。

  2. 刪除你的「WithActionApi」規則的defaults: new { id = System.Web.Http.RouteParameter.Optional }參數,因爲一旦id是可選的,url就像「/ api/{part1}/{part2}」永遠不會進入「DefaultApi」。

  3. 添加一個名爲動作到你的「DefaultApi」告訴的路線引擎行動進入哪個。否則,一旦在控制器中有多個操作,引擎將不知道使用哪一個操作,並拋出「找到與請求匹配的多個操作:...」。然後,使其與您的Get方法匹配,請使用ActionNameAttribute

所以,你的路線應該是這樣的:

// Map this rule first 
RouteTable.Routes.MapRoute(
    "WithActionApi", 
    "api/{controller}/{action}/{id}" 
); 

RouteTable.Routes.MapRoute(
    "DefaultApi", 
    "api/{controller}/{id}", 
    new { action="DefaultAction", id = System.Web.Http.RouteParameter.Optional } 
); 

而且你的控制器:

[ActionName("DefaultAction")] //Map Action and you can name your method with any text 
public string Get(int id) 
{ 
    return "object of id id"; 
}   

[HttpGet] 
public IEnumerable<string> ByCategoryId(int id) 
{ 
    return new string[] { "byCategory1", "byCategory2" }; 
} 
+1

我試過上面的建議,一切都按預期工作。非常感謝你的「祕密」。 –

+0

在你的答案的第2點,如果'id'是可選的,那麼如果'WithActionApi'路由沒有找到匹配的動作,那麼像'/ api/{part1}/{part2}'這樣的URL仍然可以進入'DefaultApi'路由。如果我錯了,請糾正我。 – orad

33

您可以屬性的幫助您解決問題路由

控制器

[Route("api/category/{categoryId}")] 
public IEnumerable<Order> GetCategoryId(int categoryId) { ... } 

jQuery中URI

api/category/1 

路由配置

using System.Web.Http; 

namespace WebApplication 
{ 
    public static class WebApiConfig 
    { 
     public static void Register(HttpConfiguration config) 
     { 
      // Web API routes 
      config.MapHttpAttributeRoutes(); 

      // Other Web API configuration not shown. 
     } 
    } 
} 

和默認路由工作作爲默認慣例爲基礎的路由

控制器

public string Get(int id) 
    { 
     return "object of id id"; 
    } 

URI jQuery中

/api/records/1 

路由配置

public static class WebApiConfig 
{ 
    public static void Register(HttpConfiguration config) 
    { 
     // Attribute routing. 
     config.MapHttpAttributeRoutes(); 

     // Convention-based routing. 
     config.Routes.MapHttpRoute(
      name: "DefaultApi", 
      routeTemplate: "api/{controller}/{id}", 
      defaults: new { id = RouteParameter.Optional } 
     ); 
    } 
} 

評論文章以獲取更多信息Attribute routing and onvention-based routing here & this

+0

上帝的回答。但是我們不能添加所有,api/{controller}/{action}/{id}以及api/{controller}/{id}嗎? – karim

+0

屬性路由definitly解決了這個問題。重要的一點是:在Web API 2之前,Web API項目模板生成的代碼如下所示: protected void Application_Start() {WebApiConfig.Register(GlobalConfiguration.Configuration); }如果啓用了屬性路由,則此代碼將引發異常。如果您升級現有的Web API項目以使用屬性路由,請確保將此配置代碼更新爲以下內容: protected void Application_Start() {GlobalConfiguration.Configure(WebApiConfig.Register); } – Deeb