public class WebApiControllerSelector : 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 WebApiControllerSelector(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.ToLower()); // 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; } // 取路由相应值 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); } //从Route中读取命名空间名称和控制器名称 string controllerName = GetRouteVariable<string>(routeData, ControllerKey).ToLower(); if (controllerName == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } HttpControllerDescriptor controllerDescriptor; //获取版本号 var version = GetVersionFromAcceptHeaderVersion(request); var versionedControllerName = string.Concat(controllerName, version).ToLower(); string versionkey = versionedControllerName; //寻找匹配项 HttpControllerDescriptor versionedControllerDescriptor; //如果命名空间名称为空,调用BaseRoute,反之采用DefaultApi string namespaceName = GetRouteVariable<string>(routeData, NamespaceKey); string key = controllerName; if (namespaceName != null) { key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", namespaceName, controllerName); versionkey = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", namespaceName, versionedControllerName); if (_controllers.Value.TryGetValue(key, out controllerDescriptor)) { if (_controllers.Value.TryGetValue(versionkey, out versionedControllerDescriptor)) { return versionedControllerDescriptor; } return controllerDescriptor; } else if (_duplicates.Contains(key)) { throw new HttpResponseException( request.CreateErrorResponse(HttpStatusCode.InternalServerError, "该请求有多个控制器匹配,请检查路由配置")); } else { throw new HttpResponseException(HttpStatusCode.NotFound); } } else { var basecontroller = _controllers.Value.Where(p => p.Key.ToLower().EndsWith(key)); if (basecontroller.Any()) { if (basecontroller.Count() > 1) { throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.InternalServerError,"该请求有多个控制器匹配,请检查路由配置")); } controllerDescriptor = basecontroller.FirstOrDefault().Value; var baseVersioncontroller = _controllers.Value.Where(p => p.Key.ToLower().EndsWith(versionkey)); if (baseVersioncontroller.Any()) { return baseVersioncontroller.FirstOrDefault().Value; } return controllerDescriptor; } else { throw new HttpResponseException(HttpStatusCode.NotFound); } } } public IDictionary<string, HttpControllerDescriptor> GetControllerMapping() { return _controllers.Value; } /// <summary> /// 添加版本控制 /// </summary> /// <param name="request"></param> /// <returns></returns> private string GetVersionFromAcceptHeaderVersion(HttpRequestMessage request) { var acceptHeader = request.Headers.Accept; if (acceptHeader.Any()) { var format = acceptHeader.First(); format.MediaType = JsonMediaTypeFormatter.DefaultMediaType.MediaType; } var heads = HttpContext.Current.Request.Headers; if (heads.AllKeys.Contains("Version")) { return heads.GetValues("Version").FirstOrDefault(); } return "1"; } }
config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{namespace}/{id}/{controller}/{subid}/{action}", defaults: new { subid = RouteParameter.Optional, action = RouteParameter.Optional } ); config.Routes.MapHttpRoute( name: "AllApi", routeTemplate: "api/{namespace}/{controller}", defaults: new { } ); config.Services.Replace(typeof(IHttpControllerSelector), new WebApiControllerSelector(config));