2014-01-24 72 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

我在这里遇到同样的问题,我想知道这将在未来发生变化吗? 我遇到的问题是,当你在路由之间有非常不同的依赖关系时,“合并”控制器可能很难单元测试。 –