1. public class WebApiControllerSelector : IHttpControllerSelector
  2. {
  3. private const string NamespaceKey = "version";
  4. private const string ControllerKey = "controller";
  5.  
  6. private readonly HttpConfiguration _configuration;
  7. private readonly Lazy<Dictionary<string, HttpControllerDescriptor>> _controllers;
  8. private readonly HashSet<string> _duplicates;
  9.  
  10. public WebApiControllerSelector(HttpConfiguration config)
  11. {
  12. _configuration = config;
  13. _duplicates = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
  14. _controllers = new Lazy<Dictionary<string, HttpControllerDescriptor>>(InitializeControllerDictionary);
  15. }
  16.  
  17. private Dictionary<string, HttpControllerDescriptor> InitializeControllerDictionary()
  18. {
  19. var dictionary = new Dictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase);
  20.  
  21. // Create a lookup table where key is "namespace.controller". The value of "namespace" is the last
  22. // segment of the full namespace. For example:
  23. // MyApplication.Controllers.V1.ProductsController => "V1.Products"
  24. IAssembliesResolver assembliesResolver = _configuration.Services.GetAssembliesResolver();
  25. IHttpControllerTypeResolver controllersResolver = _configuration.Services.GetHttpControllerTypeResolver();
  26.  
  27. ICollection<Type> controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver);
  28.  
  29. foreach (Type t in controllerTypes)
  30. {
  31. var segments = t.Namespace.Split(Type.Delimiter);
  32.  
  33. // For the dictionary key, strip "Controller" from the end of the type name.
  34. // This matches the behavior of DefaultHttpControllerSelector.
  35. var controllerName = t.Name.Remove(t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length);
  36.  
  37. var key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", segments[segments.Length - ], controllerName.ToLower());
  38.  
  39. // Check for duplicate keys.
  40. if (dictionary.Keys.Contains(key))
  41. {
  42. _duplicates.Add(key);
  43. }
  44. else
  45. {
  46. dictionary[key] = new HttpControllerDescriptor(_configuration, t.Name, t);
  47. }
  48. }
  49.  
  50. // Remove any duplicates from the dictionary, because these create ambiguous matches.
  51. // For example, "Foo.V1.ProductsController" and "Bar.V1.ProductsController" both map to "v1.products".
  52. foreach (string s in _duplicates)
  53. {
  54. dictionary.Remove(s);
  55. }
  56. return dictionary;
  57. }
  58.  
  59. // 取路由相应值
  60. private static T GetRouteVariable<T>(IHttpRouteData routeData, string name)
  61. {
  62. object result = null;
  63. if (routeData.Values.TryGetValue(name, out result))
  64. {
  65. return (T)result;
  66. }
  67. return default(T);
  68. }
  69.  
  70. //匹配相应路由
  71. public HttpControllerDescriptor SelectController(HttpRequestMessage request)
  72. {
  73. IHttpRouteData routeData = request.GetRouteData();
  74. if (routeData == null)
  75. {
  76. throw new HttpResponseException(HttpStatusCode.NotFound);
  77. }
  78.  
  79. //从Route中读取命名空间名称和控制器名称
  80. string controllerName = GetRouteVariable<string>(routeData, ControllerKey).ToLower();
  81. if (controllerName == null)
  82. {
  83. throw new HttpResponseException(HttpStatusCode.NotFound);
  84. }
  85.  
  86. HttpControllerDescriptor controllerDescriptor;
  87. //获取版本号
  88. var version = GetVersionFromAcceptHeaderVersion(request);
  89. var versionedControllerName = string.Concat(controllerName, version).ToLower();
  90. string versionkey = versionedControllerName;
  91. //寻找匹配项
  92. HttpControllerDescriptor versionedControllerDescriptor;
  93.  
  94. //如果命名空间名称为空,调用BaseRoute,反之采用DefaultApi
  95. string namespaceName = GetRouteVariable<string>(routeData, NamespaceKey);
  96. string key = controllerName;
  97. if (namespaceName != null)
  98. {
  99. key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", namespaceName, controllerName);
  100. versionkey = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", namespaceName, versionedControllerName);
  101. if (_controllers.Value.TryGetValue(key, out controllerDescriptor))
  102. {
  103. if (_controllers.Value.TryGetValue(versionkey, out versionedControllerDescriptor))
  104. {
  105. return versionedControllerDescriptor;
  106. }
  107.  
  108. return controllerDescriptor;
  109. }
  110. else if (_duplicates.Contains(key))
  111. {
  112. throw new HttpResponseException(
  113. request.CreateErrorResponse(HttpStatusCode.InternalServerError,
  114. "该请求有多个控制器匹配,请检查路由配置"));
  115. }
  116. else
  117. {
  118. throw new HttpResponseException(HttpStatusCode.NotFound);
  119. }
  120. }
  121. else
  122. {
  123. var basecontroller = _controllers.Value.Where(p => p.Key.ToLower().EndsWith(key));
  124. if (basecontroller.Any())
  125. {
  126. if (basecontroller.Count() > )
  127. {
  128. throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.InternalServerError, "该请求有多个控制器匹配,请检查路由配置"));
  129. }
  130. controllerDescriptor = basecontroller.FirstOrDefault().Value;
  131. var baseVersioncontroller = _controllers.Value.Where(p => p.Key.ToLower().EndsWith(versionkey));
  132. if (baseVersioncontroller.Any())
  133. {
  134. return baseVersioncontroller.FirstOrDefault().Value;
  135. }
  136. return controllerDescriptor;
  137. }
  138. else
  139. {
  140. throw new HttpResponseException(HttpStatusCode.NotFound);
  141. }
  142. }
  143. }
  144.  
  145. public IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
  146. {
  147. return _controllers.Value;
  148. }
  149.  
  150. /// <summary>
  151. /// 添加版本控制
  152. /// </summary>
  153. /// <param name="request"></param>
  154. /// <returns></returns>
  155. private string GetVersionFromAcceptHeaderVersion(HttpRequestMessage request)
  156. {
  157. var acceptHeader = request.Headers.Accept;
  158. if (acceptHeader.Any())
  159. {
  160. var format = acceptHeader.First();
  161. format.MediaType = JsonMediaTypeFormatter.DefaultMediaType.MediaType;
  162. }
  163.  
  164. var heads = request.Headers;
  165. if (heads.Contains("Version"))
  166. {
  167. return heads.GetValues("Version").FirstOrDefault();
  168. }
  169. return "";
  170. }
  171. }

路由:

  1. // Configure Web API for self-host.
  2.  
  3. Config.Routes.MapHttpRoute(
  4. name: "DefaultApi",
  5. routeTemplate: "api/oauth2/{version}/{controller}/{action}",
  6. defaults: new { controller = RouteParameter.Optional, action = RouteParameter.Optional }
  7. );
  8.  
  9. Config.Services.Replace(typeof(IHttpControllerSelector), new WebApiControllerSelector(Config));

Owin WebApi版本控制的更多相关文章

  1. ASP.NET Linux部署(2) - MS Owin + WebApi + Mono + Jexus

    ASP.NET Linux部署(2) - MS Owin + WebApi + Mono + Jexus 本文承接我的上一篇博文: ASP.NET 5 Linux部署,那篇文章主要是针对最新的ASP. ...

  2. (转)基于OWIN WebAPI 使用OAuth授权服务【客户端模式(Client Credentials Grant)】

    适应范围 采用Client Credentials方式,即应用公钥.密钥方式获取Access Token,适用于任何类型应用,但通过它所获取的Access Token只能用于访问与用户无关的Open ...

  3. 基于OWIN WebAPI 使用OAuth授权服务【客户端验证授权(Resource Owner Password Credentials Grant)】

    适用范围 前面介绍了Client Credentials Grant ,只适合客户端的模式来使用,不涉及用户相关.而Resource Owner Password Credentials Grant模 ...

  4. 基于OWIN WebAPI 使用OAuth授权服务【客户端模式(Client Credentials Grant)】

    适应范围 采用Client Credentials方式,即应用公钥.密钥方式获取Access Token,适用于任何类型应用,但通过它所获取的Access Token只能用于访问与用户无关的Open ...

  5. ASP.NET Core WebApi版本控制

    前言: 在日常项目开发中,随着项目需求不断的累加.不断的迭代:项目服务接口需要向下兼容历史版本:前些时候就因为Api接口为做版本管理导致接口对低版本兼容处理不友好. 最近就像了解下如何实现WebApi ...

  6. 【干货】基于Owin WebApi 使用OAuth2进行客户端授权服务

    前言:采用Client Credentials方式,即密钥key/password,场景一般是分为客户端限制必须有权限才能使用的模块,这和微信公众号开放平台很类似. 让用户通过客户端去获取自己的tok ...

  7. 电商系统架构总结4(webapi 版本控制)

    为了 顺利迭代升级,web api 在维护过程是不断升级的,但用户是不能强迫他们每次都跟随你去升级,这样会让用户不胜其烦.为了保证不同版本的客户端能同时兼容,在web api接口上加入版本控制就很有必 ...

  8. Asp.net WebApi版本控制

    有关web api的版本控制网上有很多,如Web API 版本控制的几种方式 Web API 版本化的介绍 但是具体的code并不多,或者说可以run的demo 不多. 版本控制如果项目一开始还好做关 ...

  9. Owin WebAPI上传文件

    Owin是微软出了几年的东东了,一直没时间学习.大概了解了下,是一个脱离IIS环境,快速搭建WebAPI服务的东西. 刚好想尝试下尽量脱离IIS创建简单快捷配置的项目,就是用了Nginx+Owin的模 ...

随机推荐

  1. Java考试题之六

    QUESTION 134 Given:11. class Snoochy {12. Boochy booch;13. public Snoochy() { booch = new Boochy(thi ...

  2. Android Studio aidl文件路径自定义问题

    1.aidl旧文件夹中添加的内容无法编译 sourceSets中主要是把把src/main/aidl文件也作为java.srcDirs, resources.srcDirs,这样当编译程序时,AIDL ...

  3. Map / HashMap 获取Key值的方法

    方法1:keySet()HashMap hashmp = ne HashMap();hashmp.put("aa", "111");Set set = hash ...

  4. 【纪中集训2019.3.23】Deadline

    题意 描述 一个二分图\((A,B)\),每个点额外有一个颜色0或者1: 匹配时,只能相同颜色的点匹配: 给出\(A\)中的颜色,问如何分配\(B\)种的颜色使得\((A,B)\)的最大匹配最小: 范 ...

  5. codeforces contest 1111

    A. Superhero Transformation 题意: 元音和元音,辅音和辅音字母之间可以互相转换,问两个字符串是否想同: 题解:直接判断即可: #include<bits/stdc++ ...

  6. C#线程篇---你所不知道的线程池(4)

    线程的创建和销毁都要耗费大量的时间,有什么更好的办法?用线程池! 太多的线程浪费内存资源,有什么更好的办法?用线程池! 太多线程有损性能,有什么更好的办法?用线程池!(⊙_⊙)? 线程池是什么?继前三 ...

  7. HDU 6040 stl

    Hints of sd0061 Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others ...

  8. 四、java面向对象编程_2

    目录 六.对象的创建和使用 七.this关键字 八.static关键字 九.package和import语句 十.类的继承 十一.访问控制 十二.方法的重写 十三.super关键字 十四.继承中的构造 ...

  9. vue写template的4种形式

    1.template标签(非单文件组件) <template id="t1"> <h2>66666666</h2> </template& ...

  10. P2831 愤怒的小鸟

    P2831 愤怒的小鸟 从 \((0, 0)\) 发射一只鸟, 轨迹满足抛物线, 问最少几只鸟可以打完 \(n <= 18\) 只猪 错误日志: 处理抛物线数组没有初始化 Solution 数据 ...