Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/288.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/asp.net/34.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 具有路由属性的控制器名称不明确:用于版本控制的控制器具有相同名称和不同命名空间_C#_Asp.net_Asp.net Web Api_Asp.net Web Api2_Asp.net Web Api Routing - Fatal编程技术网

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

C# 具有路由属性的控制器名称不明确:用于版本控制的控制器具有相同名称和不同命名空间,c#,asp.net,asp.net-web-api,asp.net-web-api2,asp.net-web-api-routing,C#,Asp.net,Asp.net Web Api,Asp.net Web Api2,Asp.net Web Api Routing,我正在尝试添加API版本控制,我的计划是为不同名称空间中的每个版本创建一个控制器。我的项目结构如下所示(注意:每个版本没有单独的区域) 我正在为路线使用RoutingAttribute。 因此,版本0中的ProjectController具有路由为的函数 namespace MyProject.Controllers.Version0 { class ProjectController : BaseController { ... [Route(api/user

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

我正在为路线使用RoutingAttribute。 因此,版本0中的ProjectController具有路由为的函数

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

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

     [Route(api/v1/users/project/getProjects/{projectId})]
     public async GetProjects(string projectId) 
     {
      ...
     }
  }
}
版本1中的ProjectController具有路由为的功能

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

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

     [Route(api/v1/users/project/getProjects/{projectId})]
     public async GetProjects(string projectId) 
     {
      ...
     }
  }
}
但是,当我尝试点击服务时,却发现404未找到

如果我将控制器重命名为具有唯一名称(Project1Controller和Project2Controller),则路由工作正常。但是,为了简单起见,我尽量避免重命名

我通过这个链接解决了这个问题,但没有任何帮助。我确实创造了一些领域,但仍然没有成功。在global.aspx文件中添加路由逻辑没有帮助。名称空间也不起作用。

上面的链接建议创建区域,但属性路由不支持按链接的区域:

还有别的解决办法吗?有路由属性的bug


谢谢大家!

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

您的第一个链接指向MVC路由,带有区域。官方不支持WebAPI的区域,尽管您可以尝试创建类似的区域。然而,即使您尝试这样做,您也会得到相同的错误,因为在wich Web API中查找控制器的方式没有考虑控制器的名称空间

因此,开箱即用,它永远不会起作用

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

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

如果您搜索“自定义web api控制器选择器”,您会发现许多示例,但我发现这正是您的问题最有趣的地方:

这个实现也很有趣:

  • (感谢Robin van der Knaap更新此断开的链接)
如您所见,基本上您需要:

  • 实现您自己的
    IHttpControllerSelector
    ,它考虑名称空间以查找控制器,以及名称空间路由变量以选择其中一个
  • 使用此via Web API配置替换原始选择器

我知道这个问题已经得到了回答,并且已经被原海报所接受。然而,如果你像我一样,需要使用属性路由,并尝试了建议的答案,你会知道它不会完全工作

当我尝试此操作时,我发现它实际上缺少了通过调用
HttpConfiguration
类的扩展方法
maphttpatributeroutes
生成的路由信息:

config.MapHttpAttributeRoutes();
这意味着替换
IHttpControllerSelector
实现的方法
SelectController
实际上从未被调用,这就是请求生成http 404响应的原因

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

    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);
    }
private Dictionary InitializeCache()
{
返回此.u configuration.Services.GetHttpControllerTypeResolver().GetControllerTypes(此.u configuration.Services.GetAssembliesResolver()).GroupBy((Func)(t=>t.Name.Substring(0,t.Name.Length-DefaultHttpControllerSelector.ControllerSuffix.Length)),(IEqualityComparer)StringComparer.OrderAlignorCase)。ToDictionary((Func)(g=>g.Key),(Func)(g=>g.ToLookup((Func)(t=>t.Namespace??string.Empty),(IEqualityComparer)StringComparer.OrdinalIgnoreCase)),(IEqualityComparer)StringComparer.OrdinalIgnoreCase);
}
在这段代码中,您将看到它是按类型名分组的,而不带名称空间。
DefaultHttpControllerSelector
类在为每个控制器建立
HttpControllerDescriptor
的内部缓存时使用此功能。使用
MapHttpAttribute路由
方法时,它使用另一个名为
AttributeRoutingMapper
的内部类,该类是
System.Web.Http.Routing
命名空间的一部分。此类使用
IHttpControllerSelector
GetControllerMapping
方法来配置路由


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

基于@JotaBe answer,我开发了
IHttpControllerSelector
,它允许控制器(在我的例子中,那些被标记为
[RoutePrefix]
属性的控制器)映射到它们的全名(名称空间和名称)

使用系统;
使用System.Collections.Generic;
使用System.Linq;
Net系统;
使用System.Net.Http;
使用System.Web.Http;
使用System.Web.Http.Controller;
使用System.Web.Http.Dispatcher;
使用System.Web.Http.Routing;
/// 
///允许使用具有相同名称的多个控制器(显然在不同的名称空间中)
///通过在控制器标识符前面加上名称空间(如果它们具有[RoutePrefix]属性)。
///允许基于属性的控制器与显式路由控制器混合而不发生冲突。
/// 
公共类名称空间HttpControllerSelector:DefaultHttpControllerSelector
{
私有HttpConfiguration\u配置;
专用只读懒控制器;
公共名称空间httpcontrollerselector(Htt