using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Web.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; using System.Web.Http.Routing; namespace Lottomat.SOA.API.Selector { /// /// WebApi版本控制 /// public class WebApiControllerSelector : IHttpControllerSelector { private const string VersionKey = "version"; private const string ControllerKey = "controller"; private readonly HttpConfiguration _configuration; private readonly Lazy> _controllers; private readonly HashSet _duplicates; private WebApiControllerSelector() { } /// /// 构造 /// /// public WebApiControllerSelector(HttpConfiguration config) { _configuration = config; _duplicates = new HashSet(StringComparer.OrdinalIgnoreCase); _controllers = new Lazy>(InitializeControllerDictionary); } /// /// 初始化字典 /// /// private Dictionary InitializeControllerDictionary() { var dictionary = new Dictionary(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 controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver); foreach (Type t in controllerTypes) { if (t.Namespace != null) { 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); string version = segments[segments.Length - 1]; var key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", version, controllerName); if (version == "Controllers") { key = String.Format(CultureInfo.InvariantCulture, "{0}", 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; } /// /// 取路由相应值 /// /// /// /// /// private static T GetRouteVariable(IHttpRouteData routeData, string name) { if (routeData.Values.TryGetValue(name, out object result)) { return (T)result; } return default(T); } /// /// 匹配相应路由 /// /// /// public HttpControllerDescriptor SelectController(HttpRequestMessage request) { IHttpRouteData routeData = request.GetRouteData(); if (routeData == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } //获取版本信息 string version = GetRouteVariable(routeData, VersionKey); if (string.IsNullOrEmpty(version)) { version = GetVersionFromAcceptHeaderVersion(request); } //获取控制器名称 string controllerName = GetRouteVariable(routeData, ControllerKey); if (string.IsNullOrEmpty(controllerName)) { throw new HttpResponseException(HttpStatusCode.NotFound); } else { // Find a matching controller. string key = String.Format(CultureInfo.InvariantCulture, "{0}", controllerName); if (!string.IsNullOrEmpty(version)) { key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", version.ToUpper(), controllerName); } if (_controllers.Value.TryGetValue(key, out HttpControllerDescriptor controllerDescriptor)) { return controllerDescriptor; } else if (_duplicates.Contains(key)) { throw new HttpResponseException( request.CreateErrorResponse(HttpStatusCode.InternalServerError, "找到了与此请求相匹配的多个控制器.")); } else { throw new HttpResponseException( request.CreateErrorResponse(HttpStatusCode.NotFound, "找不到请求资源,请检查请求地址是否正确.")); //throw new HttpResponseException(HttpStatusCode.NotFound); } } } /// /// 获取映射信息 /// /// public IDictionary GetControllerMapping() { return _controllers.Value; } /// /// 从请求头里面获取版本信息 /// /// /// private string GetVersionFromAcceptHeaderVersion(HttpRequestMessage request) { if (request.Headers.Contains(VersionKey)) { string versionHeader = request.Headers.GetValues(VersionKey).FirstOrDefault(); if (versionHeader != null) { return versionHeader; } } var acceptHeader = request.Headers.Accept; foreach (var mime in acceptHeader) { if (mime.MediaType == "application/json" || mime.MediaType == "text/html") { NameValueHeaderValue version = mime.Parameters .FirstOrDefault(v => v.Name.Equals(VersionKey, StringComparison.OrdinalIgnoreCase)); if (version != null) { return version.Value; } return string.Empty; } } return string.Empty; } } }