2014-01-24 71 views
0

我在我的項目中使用Web API 2屬性路由來爲我的數據提供JSON接口。我面臨着控制器選擇的怪異行爲,並未決定它是一個錯誤還是一個功能:) 讓我來描述一下我的方法。Web API 2屬性路由控制器選擇

我想借助屬性路由來模擬OData語法(由於設計原則,直接OData使用已被拒絕)。例如,要獲取id = 5的實體,我使用HTTP GET請求來訪問URI http://mydomain.com/api/Entity(5)。我期望與HTTP PUT動詞使用相同的URI來更新實體。這是旅程的開始...

我想單獨控制器獲取實體(FirstController在下面提供的示例中提供)和另一個用於修改實體(SecondController)。兩個控制器處理相同的URI(例如http://mydomain.com/api/Entity(5)),唯一的區別是與URI一起使用的HTTP動詞 - GET應該由FirstController處理,PUT應該由SecondController處理。但是URI並沒有被處理。而不是HTTP 404錯誤返回。 當我將GET和PUT操作「合併」到一個控制器(在FirstController中註釋掉)時,兩個動詞都被正確處理。 我使用的是IIS Express,並且所有常規路由都被禁用,只有屬性路由負責。

它看起來像控制器選擇過程不適用於HTTP動詞。換句話說,HttpGetHttpPut屬性只是限制操作使用,但它們不作爲控制器選擇期間的標準。我對MVC/Web API基礎知識並不熟悉,所以讓我問你我的一個大問題:

以上描述的行爲是MVC/Web API 2有意實現的功能還是需要修復的錯誤?

如果它被認爲是一個功能,它會阻止我遵循設計原則。我可以和「合併」的控制者一起生活,但仍然認爲這是一種不好的做法...... 或者我錯過了我的思路?

我的環境設置:

  • 的Windows 7(使用Oracle的VirtualBox虛擬機)
  • 的Visual Studio 2013
  • .NET 4.5。1
  • 的Web API 2

以下是實現FirstController類:

public class FirstController : ApiController 
{ 
    [HttpGet] 
    [Route("api/Entity({id:int})")] 
    public Output GetEntity(int id) 
    { 
    Output output = new Output() { Id = id, Name = "foo" }; 

    return output; 
    } 

    //[HttpPut] 
    //[Route("api/Entity({id:int})")] 
    //public Output UpdateEntity(int id, UpdateEntity command) 
    //{ 
    // Output output = new Output() { Id = id, Name = command.Name }; 

    // return output; 
    //} 
} 

以下是實現SecondController類:

public class SecondController : ApiController 
{ 
    [HttpPut] 
    [Route("api/Entity({id:int})")] 
    public Output UpdateEntity(int id, UpdateEntity command) 
    { 
    Output output = new Output() { Id = id, Name = command.Name }; 

    return output; 
    } 
} 

以下是實現一個控制檯應用程序來測試所描述的行爲:

class Program 
{ 
    static void Main(string[] args) 
    { 
    // HTTP client initialization 
    HttpClient httpClient = new HttpClient(); 
    httpClient.BaseAddress = new Uri("http://localhost:1567"); 
    httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 

    // HTTP GET - FirstController.GetEntity 
    HttpResponseMessage getEntityResponse = httpClient.GetAsync("/api/Entity(5)").Result; 
    Output getOutput = getEntityResponse.Content.ReadAsAsync<Output>().Result; 

    // HTTP PUT - SecondController.UpdateEntity 
    UpdateEntity updateCommand = new UpdateEntity() { Name = "newEntityname" }; 
    HttpResponseMessage updateEntityResponse = httpClient.PutAsJsonAsync("/api/Entity(10)", updateCommand).Result; 
    Output updateOutput = updateEntityResponse.Content.ReadAsAsync<Output>().Result; 
    } 
} 

完成,以下是使用的DTO:提前爲您的答覆

public class UpdateEntity 
{ 
    public string Name { get; set; } 
} 


public class Output 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

感謝,

月Kacina

+0

對於「Google搜索」完成,這裏是您在這種情況下的錯誤: 「找到了與URL匹配的多個控制器類型,如果多個控制器上的屬性路由匹配請求的URL,就會發生這種情況。 –

+0

我已經看到了最好的問題之一,很好地完成:) – Scozzard

回答

1

這樣的設計是有意爲之,因爲我們認爲這是一個錯誤用戶在不同的控制器上將具有相同的路由模板的情況,這可能導致選擇過程中的不明確性。

此外,如果我們撇開屬性路由,這將如何與常規路由工作?讓我們想象我們有兩條常規路線,其中第一條針對FirstController,第二條針對SecondController。現在,如果請求url與api/Entity(5)類似,那麼Web API將始終與路由表中的第一個路由匹配,該路由始終會觸發FirstController並永遠不會到達SecondController。請記住,一旦Web API匹配它嘗試去直到動作選擇過程的路由,並且如果動作選擇過程沒有導致被選擇的動作,則將錯誤響應發送到客戶端。您可能認爲如果在一個控制器中未選擇某個操作,則Web API會將其路由到路由配置中的下一個控制器。這是不正確的。

路由探測只發生一次,如果它導致匹配,那麼接下來的步驟發生......那就是控制器和動作選擇。希望這可以幫助。

+0

感謝您的解釋。我一直假設這一點,只是想讓這個假設得到證實。 –

+0

我在這裏遇到同樣的問題,我想知道這將在未來發生變化嗎? 我遇到的問題是,當你在路由之間有非常不同的依賴關係時,「合併」控制器可能很難單元測試。 –