Ambiguous Controller Names with Routing attributes: controllers with same name and different namespace for versioning

孤者浪人 提交于 2019-11-28 23:22:55

First, Web API routing, and MVC routing doesn't work exactly in the same way.

Your first link points to MVC routing, with areas. Areas are not officially supported for Web API, although you can try to make something similar to them. However, even if you try to do something like that, you'll get the same error, because the way in wich Web API looks for a controller doesn't takes into account the controller's namespace.

So, out of the box, it will never work.

However, you can modify most Web API behaviors, and this is not an exception.

Web API uses a Controller Selector to get the desired controller. The behavior explained above is the behavior of the DefaultHttpControllerSelector, which comes with Web API, but you can implement your own selector to replace the default one, and support new behaviors.

If you google for "custom web api controller selector" you'll find many samples, but I find this the most interesting for exactly your problem:

This implementation is also interesting:

As you see there, basically you need to:

  • implement your own IHttpControllerSelector, which takes into account namespaces to find the controllers, and the namespaces route variable, to choose one of them.
  • replace the original selector with this via Web API configuration.

I know this was answered a while a go and has already been accepted by the original poster. However if you are like me and require the use of attribute routing and have tried the suggested answer you will know that it wont quite work.

When I tried this I found out that it was actually missing the routing information that should have been generated by calling the extension method MapHttpAttributeRoutes of theHttpConfiguration class:

config.MapHttpAttributeRoutes();

This meant that the method SelectController of the replacement IHttpControllerSelector implementation never actually gets called and is why the request produces a http 404 response.

The issue is caused by an internal class called HttpControllerTypeCache which is an internal class in the System.Web.Http assembly under the System.Web.Http.Dispatcher namespace. The code in question is the following:

    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);
    }

You will see in this code that it is grouping by the type name without the namespace. The DefaultHttpControllerSelector class uses this functionality when it builds up an internal cache of HttpControllerDescriptor for each controller. When using the MapHttpAttributeRoutes method it use another internal class called AttributeRoutingMapper which is part of the System.Web.Http.Routing namespace. This class uses the method GetControllerMapping of the IHttpControllerSelector in order to configure the routes.

So if you are going to write a custom IHttpControllerSelector then you need to overload the GetControllerMapping method for it to work. The reason I mention this is that none of the implementations I have seen on the internet does this.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!