这个问题来源于我想在 Web API 中使用相同的控制器名称(Controller)在不一样的命名空间下,可是 Web API 的默认 路由(Route) 机制是会忽略命名空间的不一样的,若是这样作,会看到如下提示:web
找到多个与名为“XXX”的控制器匹配的类型。若是为此请求(“{namespace}/{controller}/{action}”)提供服务的路由找到多个控制器,而且这些控制器是使用相同的名称但不一样的命名空间定义的(这不受支持),则会发生这种状况。api
在 ASP.NET MVC 中,能够经过创建 区域(Area) 来解决这种问题,但 Web API 并无区域这种东西。app
不过官方早已给出了解决方案,只是并无做为 Web API 的一部分直接集成。不知道是出于什么考虑。this
原文连接:http://blogs.msdn.com/b/webdev/archive/2013/03/08/using-namespaces-to-version-web-apis.aspxspa
主要思路就是本身从新实现一个能够识别命名空间的路由选择器,而后替换掉系统默认的路由选择器便可。这个命名空间选择器官方也已经帮咱们实现,而且提供了完整的项目演示示例。code
将此类的实现加入到项目中,并在初始化 Web API 路由时进行替换,在设置路由模板的时候,加入相应的 {namespace} 参数便可:blog
public static class WebApiConfig { public static void Register(HttpConfiguration config) { config.MapHttpAttributeRoutes(); config.Services.Replace(typeof(IHttpControllerSelector), new NamespaceHttpControllerSelector(config)); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "{namespace}/{controller}/{action}" ); } }
最后,附上官方 NamespaceHttpControllerSelector 类的实现代码(出处请见上述连接):ip
public class NamespaceHttpControllerSelector : IHttpControllerSelector { private const string NamespaceKey = "namespace"; private const string ControllerKey = "controller"; private readonly HttpConfiguration _configuration; private readonly Lazy<Dictionary<string, HttpControllerDescriptor>> _controllers; private readonly HashSet<string> _duplicates; public NamespaceHttpControllerSelector(HttpConfiguration config) { _configuration = config; _duplicates = new HashSet<string>(StringComparer.OrdinalIgnoreCase); _controllers = new Lazy<Dictionary<string, HttpControllerDescriptor>>(InitializeControllerDictionary); } private Dictionary<string, HttpControllerDescriptor> InitializeControllerDictionary() { var dictionary = new Dictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase); // Create a lookup table where key is "namespace.controller". The value of "namespace" is the last // segment of the full namespace. For example: // MyApplication.Controllers.V1.ProductsController => "V1.Products" IAssembliesResolver assembliesResolver = _configuration.Services.GetAssembliesResolver(); IHttpControllerTypeResolver controllersResolver = _configuration.Services.GetHttpControllerTypeResolver(); ICollection<Type> controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver); foreach (Type t in controllerTypes) { var segments = t.Namespace.Split(Type.Delimiter); // For the dictionary key, strip "Controller" from the end of the type name. // This matches the behavior of DefaultHttpControllerSelector. var controllerName = t.Name.Remove(t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length); var key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", segments[segments.Length - 1], controllerName); // Check for duplicate keys. if (dictionary.Keys.Contains(key)) { _duplicates.Add(key); } else { dictionary[key] = new HttpControllerDescriptor(_configuration, t.Name, t); } } // Remove any duplicates from the dictionary, because these create ambiguous matches. // For example, "Foo.V1.ProductsController" and "Bar.V1.ProductsController" both map to "v1.products". foreach (string s in _duplicates) { dictionary.Remove(s); } return dictionary; } // Get a value from the route data, if present. private static T GetRouteVariable<T>(IHttpRouteData routeData, string name) { object result = null; if (routeData.Values.TryGetValue(name, out result)) { return (T)result; } return default(T); } public HttpControllerDescriptor SelectController(HttpRequestMessage request) { IHttpRouteData routeData = request.GetRouteData(); if (routeData == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } // Get the namespace and controller variables from the route data. string namespaceName = GetRouteVariable<string>(routeData, NamespaceKey); if (namespaceName == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } string controllerName = GetRouteVariable<string>(routeData, ControllerKey); if (controllerName == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } // Find a matching controller. string key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", namespaceName, controllerName); HttpControllerDescriptor controllerDescriptor; if (_controllers.Value.TryGetValue(key, out controllerDescriptor)) { return controllerDescriptor; } else if (_duplicates.Contains(key)) { throw new HttpResponseException( request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Multiple controllers were found that match this request.")); } else { throw new HttpResponseException(HttpStatusCode.NotFound); } } public IDictionary<string, HttpControllerDescriptor> GetControllerMapping() { return _controllers.Value; } }