带有路由属性的不明确的控制器名称:具有相同名称和不同命名空间的控制器,用于版本控制

rkd*_*rkd 15 c# asp.net asp.net-web-api asp.net-web-api-routing asp.net-web-api2

我正在尝试添加API版本,我的计划是为不同命名空间中的每个版本创建一个控制器.我的项目结构如下所示(注意:每个版本没有单独的区域)

Controllers
 |
 |---Version0
 |      |
 |      |----- ProjectController.cs
 |      |----- HomeController.cs
 |
 |---Version1
       |
       |----- ProjectController.cs
       |----- HomeController.cs
Run Code Online (Sandbox Code Playgroud)

我正在使用RoutingAttribute作为路由.因此,Version0中的ProjectController具有路由功能

namespace MyProject.Controllers.Version0
{
   class ProjectController : BaseController
   {
     ...

     [Route(api/users/project/getProjects/{projectId})]
     public async GetProjects(string projectId) 
     {
       ...
     }
  }
}
Run Code Online (Sandbox Code Playgroud)

和Version1中的ProjectController具有路由功能

namespace MyProject.Controllers.Version1
{
   class ProjectController : BaseController
   {
     ...

     [Route(api/v1/users/project/getProjects/{projectId})]
     public async GetProjects(string projectId) 
     {
      ...
     }
  }
}
Run Code Online (Sandbox Code Playgroud)

但是,当我尝试使用该服务时,我得到404-NotFound.

如果我将控制器重命名为具有唯一名称(Project1Controller和Project2Controller),则路由可以正常工作.但是,我试图避免重命名以简化.

我按照此链接解决了问题,但没有帮助.我确实创造了一些领域但仍然没有成功.在global.aspx文件中添加路由逻辑没有帮助.命名空间也不起作用. http://haacked.com/archive/2010/01/12/ambiguous-controller-names.aspx/

上面的链接建议创建区域,但属性路由不支持按链接区域:http: //www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-在-Web的API-2

还有其他解决方案吗?RoutingAttributes有错误吗?

谢谢!

Jot*_*aBe 21

首先,Web API路由和MVC路由不能以完全相同的方式工作.

您的第一个链接指向带有区域的MVC路由.虽然您可以尝试制作与它们类似的内容,但Web API并未正式支持这些区域.但是,即使您尝试执行类似的操作,也会得到相同的错误,因为Web API寻找控制器的方式并没有考虑控制器的命名空间.

因此,开箱即用,它永远不会奏效.

但是,您可以修改大多数Web API行为,这不是一个例外.

Web API使用控制器选择器来获取所需的控制器.上面解释的行为是DefaultHttpControllerSelector的行为,它随Web API一起提供,但您可以实现自己的选择器来替换默认选择器,并支持新行为.

如果你谷歌搜索"自定义web api控制器选择器"你会发现很多样本,但我发现这对你的问题最有趣:

这个实现也很有趣:

如你所见,基本上你需要:

  • 实现自己的IHttpControllerSelector,它会考虑命名空间来查找控制器,以及命名空间路由变量,以选择其中一个.
  • 通过Web API配置替换原始选择器.


Tom*_*her 7

我知道这回答了一段时间,并且已经被原始海报接受了.但是,如果你像我一样需要使用属性路由并尝试了建议的答案,你会知道它不会很有效.

当我尝试这样做,我发现,它实际上是缺少应该已经通过调用扩展方法产生的路由信息MapHttpAttributeRoutes的的HttpConfiguration类:

config.MapHttpAttributeRoutes();
Run Code Online (Sandbox Code Playgroud)

这意味着SelectController替换IHttpControllerSelector实现的方法实际上永远不会被调用,这就是请求产生http 404响应的原因.

该问题是由一个内部类引起的,该类HttpControllerTypeCache是命名空间System.Web.Http下程序集中的内部类System.Web.Http.Dispatcher.有问题的代码如下:

    private Dictionary<string, ILookup<string, Type>> InitializeCache()
    {
      return this._configuration.Services.GetHttpControllerTypeResolver().GetControllerTypes(this._configuration.Services.GetAssembliesResolver()).GroupBy<Type, string>((Func<Type, string>) (t => t.Name.Substring(0, t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length)), (IEqualityComparer<string>) StringComparer.OrdinalIgnoreCase).ToDictionary<IGrouping<string, Type>, string, ILookup<string, Type>>((Func<IGrouping<string, Type>, string>) (g => g.Key), (Func<IGrouping<string, Type>, ILookup<string, Type>>) (g => g.ToLookup<Type, string>((Func<Type, string>) (t => t.Namespace ?? string.Empty), (IEqualityComparer<string>) StringComparer.OrdinalIgnoreCase)), (IEqualityComparer<string>) StringComparer.OrdinalIgnoreCase);
    }
Run Code Online (Sandbox Code Playgroud)

您将在此代码中看到它是按类型名称进行分组而没有命名空间.的DefaultHttpControllerSelector类使用该功能时,它积聚的内部高速缓存HttpControllerDescriptor为每个控制器.当使用MapHttpAttributeRoutes方法它使用另一种被称为内部类AttributeRoutingMapper,其是一部分System.Web.Http.Routing命名空间.该类使用方法GetControllerMappingIHttpControllerSelector,以配置路由.

因此,如果您要编写自定义,IHttpControllerSelector那么您需要重载该GetControllerMapping方法才能使其工作.我提到这个的原因是我在互联网上看到的所有实现都没有这样做.