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

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

Controllers | |---Version0 | | | |----- ProjectController.cs | |----- HomeController.cs | |---Version1 | |----- ProjectController.cs |----- HomeController.cs 

我正在使用RoutingAttribute作为路由。 因此,Version0中的ProjectController具有路由function

 namespace MyProject.Controllers.Version0 { class ProjectController : BaseController { ... [Route(api/users/project/getProjects/{projectId})] public async GetProjects(string projectId) { ... } } } 

Version1中的ProjectController和具有route的函数

 namespace MyProject.Controllers.Version1 { class ProjectController : BaseController { ... [Route(api/v1/users/project/getProjects/{projectId})] public async GetProjects(string projectId) { ... } } } 

但是,当我尝试使用该服务时,我得到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的一个错误?

谢谢!

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

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

所以,开箱即用,它永远不会奏效。

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

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

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

  • ASP.NET Web API:使用命名空间到版本Web API

这个实现也很有趣:

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

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

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

当我尝试这个时,我发现它实际上缺少应该通过调用HttpConfiguration类的扩展方法MapHttpAttributeRoutes生成的路由信息​​:

 config.MapHttpAttributeRoutes(); 

这意味着替换IHttpControllerSelector实现的方法SelectController实际上从未被调用,这就是请求产生http 404响应的原因。

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

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

您将在此代码中看到它按类型名称进行分组而没有命名空间。 DefaultHttpControllerSelector类在为每个控制器构建和使用HttpControllerDescriptor内部缓存时使用此function。 使用MapHttpAttributeRoutes方法时,它使用另一个名为AttributeRoutingMapper内部类,它是System.Web.Http.Routing命名空间的一部分。 此类使用IHttpControllerSelector GetControllerMapping方法来配置路由。

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