簡體   English   中英

Web API 2屬性路由控制器選擇

[英]Web API 2 Attribute Routing Controller Selection

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

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

我想有一個用於獲取實體的單獨控制器(在下面提供的示例中為FirstController )和一個用於修改實體的控制器( SecondController )。 兩個控制器都處理相同的URI(例如http://mydomain.com/api/Entity(5) ),唯一的區別是與URI一起使用的HTTP動詞FirstController應該由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; }
}

預先感謝您的回復,

揚·卡西納(Jan Kacina)

這種設計是有意的,因為我們認為這是一個錯誤情況,即用戶在不同的控制器上具有相同的路由模板,這可能在選擇過程中造成歧義。

另外,如果我們不考慮屬性路由,那么如何使用常規路由呢? 假設我們有2條常規路線,第一個路線針對FirstController,第二個路線針對SecondController。 現在,如果請求網址類似於api/Entity(5) ,則Web API將始終與路由表中的第一條路由匹配,該路由將始終命中FirstController且永遠不會到達SecondController。 請記住,一旦Web API匹配了路由,它就會嘗試執行操作選擇過程,並且如果操作選擇過程沒有導致選擇操作,則會向客戶端發送錯誤響應。 您可能假設如果未在一個控制器中選擇任何動作,則Web API會將其路由到路由配置中的下一個動作。 這是不正確的。

路由探測僅發生一次,如果結果匹配,則接下來的步驟將進行...即控制器和操作選擇。 希望這可以幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM