WebApiControllerSelector.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.Linq;
  5. using System.Net;
  6. using System.Net.Http;
  7. using System.Net.Http.Headers;
  8. using System.Web.Http;
  9. using System.Web.Http.Controllers;
  10. using System.Web.Http.Dispatcher;
  11. using System.Web.Http.Routing;
  12. namespace Lottomat.SOA.API.Selector
  13. {
  14. /// <summary>
  15. /// WebApi版本控制
  16. /// </summary>
  17. public class WebApiControllerSelector : IHttpControllerSelector
  18. {
  19. private const string VersionKey = "version";
  20. private const string ControllerKey = "controller";
  21. private readonly HttpConfiguration _configuration;
  22. private readonly Lazy<Dictionary<string, HttpControllerDescriptor>> _controllers;
  23. private readonly HashSet<string> _duplicates;
  24. private WebApiControllerSelector()
  25. {
  26. }
  27. /// <summary>
  28. /// 构造
  29. /// </summary>
  30. /// <param name="config"></param>
  31. public WebApiControllerSelector(HttpConfiguration config)
  32. {
  33. _configuration = config;
  34. _duplicates = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
  35. _controllers = new Lazy<Dictionary<string, HttpControllerDescriptor>>(InitializeControllerDictionary);
  36. }
  37. /// <summary>
  38. /// 初始化字典
  39. /// </summary>
  40. /// <returns></returns>
  41. private Dictionary<string, HttpControllerDescriptor> InitializeControllerDictionary()
  42. {
  43. var dictionary = new Dictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase);
  44. // Create a lookup table where key is "namespace.controller". The value of "namespace" is the last
  45. // segment of the full namespace. For example:
  46. // MyApplication.Controllers.V1.ProductsController => "V1.Products"
  47. IAssembliesResolver assembliesResolver = _configuration.Services.GetAssembliesResolver();
  48. IHttpControllerTypeResolver controllersResolver = _configuration.Services.GetHttpControllerTypeResolver();
  49. ICollection<Type> controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver);
  50. foreach (Type t in controllerTypes)
  51. {
  52. if (t.Namespace != null)
  53. {
  54. var segments = t.Namespace.Split(Type.Delimiter);
  55. // For the dictionary key, strip "Controller" from the end of the type name.
  56. // This matches the behavior of DefaultHttpControllerSelector.
  57. var controllerName = t.Name.Remove(t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length);
  58. string version = segments[segments.Length - 1];
  59. var key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", version, controllerName);
  60. if (version == "Controllers")
  61. {
  62. key = String.Format(CultureInfo.InvariantCulture, "{0}", controllerName);
  63. }
  64. // Check for duplicate keys.
  65. if (dictionary.Keys.Contains(key))
  66. {
  67. _duplicates.Add(key);
  68. }
  69. else
  70. {
  71. dictionary[key] = new HttpControllerDescriptor(_configuration, t.Name, t);
  72. }
  73. }
  74. }
  75. // Remove any duplicates from the dictionary, because these create ambiguous matches.
  76. // For example, "Foo.V1.ProductsController" and "Bar.V1.ProductsController" both map to "v1.products".
  77. foreach (string s in _duplicates)
  78. {
  79. dictionary.Remove(s);
  80. }
  81. return dictionary;
  82. }
  83. /// <summary>
  84. /// 取路由相应值
  85. /// </summary>
  86. /// <typeparam name="T"></typeparam>
  87. /// <param name="routeData"></param>
  88. /// <param name="name"></param>
  89. /// <returns></returns>
  90. private static T GetRouteVariable<T>(IHttpRouteData routeData, string name)
  91. {
  92. if (routeData.Values.TryGetValue(name, out object result))
  93. {
  94. return (T)result;
  95. }
  96. return default(T);
  97. }
  98. /// <summary>
  99. /// 匹配相应路由
  100. /// </summary>
  101. /// <param name="request"></param>
  102. /// <returns></returns>
  103. public HttpControllerDescriptor SelectController(HttpRequestMessage request)
  104. {
  105. IHttpRouteData routeData = request.GetRouteData();
  106. if (routeData == null)
  107. {
  108. throw new HttpResponseException(HttpStatusCode.NotFound);
  109. }
  110. //获取版本信息
  111. string version = GetRouteVariable<string>(routeData, VersionKey);
  112. if (string.IsNullOrEmpty(version))
  113. {
  114. version = GetVersionFromAcceptHeaderVersion(request);
  115. }
  116. //获取控制器名称
  117. string controllerName = GetRouteVariable<string>(routeData, ControllerKey);
  118. if (string.IsNullOrEmpty(controllerName))
  119. {
  120. throw new HttpResponseException(HttpStatusCode.NotFound);
  121. }
  122. else
  123. {
  124. // Find a matching controller.
  125. string key = String.Format(CultureInfo.InvariantCulture, "{0}", controllerName);
  126. if (!string.IsNullOrEmpty(version))
  127. {
  128. key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", version.ToUpper(), controllerName);
  129. }
  130. if (_controllers.Value.TryGetValue(key, out HttpControllerDescriptor controllerDescriptor))
  131. {
  132. return controllerDescriptor;
  133. }
  134. else if (_duplicates.Contains(key))
  135. {
  136. throw new HttpResponseException(
  137. request.CreateErrorResponse(HttpStatusCode.InternalServerError,
  138. "找到了与此请求相匹配的多个控制器."));
  139. }
  140. else
  141. {
  142. throw new HttpResponseException(
  143. request.CreateErrorResponse(HttpStatusCode.NotFound,
  144. "找不到请求资源,请检查请求地址是否正确."));
  145. //throw new HttpResponseException(HttpStatusCode.NotFound);
  146. }
  147. }
  148. }
  149. /// <summary>
  150. /// 获取映射信息
  151. /// </summary>
  152. /// <returns></returns>
  153. public IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
  154. {
  155. return _controllers.Value;
  156. }
  157. /// <summary>
  158. /// 从请求头里面获取版本信息
  159. /// </summary>
  160. /// <param name="request"></param>
  161. /// <returns></returns>
  162. private string GetVersionFromAcceptHeaderVersion(HttpRequestMessage request)
  163. {
  164. if (request.Headers.Contains(VersionKey))
  165. {
  166. string versionHeader = request.Headers.GetValues(VersionKey).FirstOrDefault();
  167. if (versionHeader != null)
  168. {
  169. return versionHeader;
  170. }
  171. }
  172. var acceptHeader = request.Headers.Accept;
  173. foreach (var mime in acceptHeader)
  174. {
  175. if (mime.MediaType == "application/json" || mime.MediaType == "text/html")
  176. {
  177. NameValueHeaderValue version = mime.Parameters
  178. .FirstOrDefault(v => v.Name.Equals(VersionKey, StringComparison.OrdinalIgnoreCase));
  179. if (version != null)
  180. {
  181. return version.Value;
  182. }
  183. return string.Empty;
  184. }
  185. }
  186. return string.Empty;
  187. }
  188. }
  189. }