标签:项目 第一个 hash reac option ping mat cti contain
很多年前就知道我软的WebApi的路由模板配置方法不支持namespaces参数的事,所以也追随我软的脚步在程序中不再造namespaces参数的设计与实现,最近小组里有人思维不够开源,想着使用namespaces参数把启动项目和Api具体实现分成两个项目,目的大概是为了保护源码,我极度排斥这种老旧思想,不过既然有人还惦念namespaces的事儿,也不妨从技术的角度出发去玩一玩。于是乎一顿大搜索后,经过学习、分析、理解、总结、设计、试验后,最终实现了让WebApi的路由配置方法像Mvc一样支持namespaces参数,具体代码如下:
第一个文件:HttpRouteCollectionExtensions,通过扩展方法的形式实现MapHttpRoute支持namespaces参数
1 public static class HttpRouteCollectionExtensions
2 {
3 public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name, string routeTemplate, object defaults, string[] namespaces)
4 {
5 return routes.MapHttpRoute(name, routeTemplate, defaults, null, null, namespaces);
6 }
7 public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name, string routeTemplate, object defaults, object constraints, HttpMessageHandler handler, string[] namespaces)
8 {
9 if (routes == null)
10 {
11 throw new ArgumentNullException("routes");
12 }
13
14 var dvd = new HttpRouteValueDictionary(defaults);
15 var cvd = new HttpRouteValueDictionary(constraints);
16 var nvd = new HttpRouteValueDictionary(new { Namespace = namespaces });
17
18 var route = routes.CreateRoute(routeTemplate, dvd, cvd, nvd, handler);
19 routes.Add(name, route);
20 return route;
21 }
22 }
View Code
第二个文件:NamespaceHttpControllerSelector,控制器的命名空间选择器,用于代替默认的选择器
1 public class NamespaceHttpControllerSelector : IHttpControllerSelector
2 {
3 private const string NamespaceKey = "Namespace";
4 private const string ControllerKey = "controller";
5
6 private readonly HttpConfiguration _configuration;
7 private readonly Lazystring, HttpControllerDescriptor>> _controllers;
8 private readonly HashSetstring> _duplicates;
9
10 public NamespaceHttpControllerSelector(HttpConfiguration config)
11 {
12 _configuration = config;
13 _duplicates = new HashSetstring>(StringComparer.OrdinalIgnoreCase);
14 _controllers = new Lazystring, HttpControllerDescriptor>>(InitializeControllerDictionary);
15 }
16
17 private Dictionarystring, HttpControllerDescriptor> InitializeControllerDictionary()
18 {
19 var dictionary = new Dictionarystring, 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 controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver);
28
29 foreach (Type t in controllerTypes)
30 {
31 // For the dictionary key, strip "Controller" from the end of the type name.
32 var controllerName = "";
33 var cItem = t.GetCustomAttribute(typeof(RoutePrefixAttribute));
34 if (cItem != null)
35 {
36 var segments = ((RoutePrefixAttribute)cItem).Prefix.Split(‘/‘);
37 controllerName = segments[segments.Length - 1];
38 }
39 else
40 {
41 // This matches the behavior of DefaultHttpControllerSelector.
42 controllerName = t.Name.Remove(t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length);
43 }
44
45 var key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", t.Namespace, controllerName);
46
47 // Check for duplicate keys.
48 if (dictionary.Keys.Contains(key))
49 {
50 _duplicates.Add(key);
51 }
52 else
53 {
54 dictionary[key] = new HttpControllerDescriptor(_configuration, controllerName, t);
55 }
56 }
57
58 // Remove any duplicates from the dictionary, because these create ambiguous matches.
59 // For example, "Foo.V1.ProductsController" and "Bar.V1.ProductsController" both map to "v1.products".
60 foreach (string s in _duplicates)
61 {
62 dictionary.Remove(s);
63 }
64 return dictionary;
65 }
66 private T GetRouteVariable(IHttpRouteData routeData, string name)
67 {
68 object result = null;
69 if (routeData.Values.TryGetValue(name, out result))
70 {
71 return (T)result;
72 }
73 return default(T);
74 }
75 private string GetControllerKey(IHttpRouteData routeData, string controllerName)
76 {
77 var keys = _controllers.Value.Keys.ToList();
78 object namespaceName = null;
79 if (routeData.Route.DataTokens == null || !routeData.Route.DataTokens.TryGetValue(NamespaceKey, out namespaceName))
80 {
81 return keys.FirstOrDefault(o => o.ToLower().EndsWith(controllerName.ToLower()));
82 }
83
84 //get the defined namespace
85 string[] namespaces = (string[])namespaceName;
86 for (int i = 0; i )
87 {
88 var nss = keys.Where(o => o.StartsWith(namespaces[i])).ToList();
89 if (nss != null && nss.Count() > 0)
90 {
91 return nss.FirstOrDefault(o => o.ToLower().EndsWith(controllerName.ToLower()));
92 }
93 }
94 return null;
95 }
96
97 public HttpControllerDescriptor SelectController(HttpRequestMessage request)
98 {
99 IHttpRouteData routeData = request.GetRouteData();
100 if (routeData == null)
101 {
102 throw new HttpResponseException(HttpStatusCode.NotFound);
103 }
104
105 // Get the namespace and controller variables from the route data.
106 string controllerName = GetRouteVariablestring>(routeData, ControllerKey);
107 if (controllerName == null)
108 {
109 throw new HttpResponseException(HttpStatusCode.NotFound);
110 }
111
112 // Find a matching controller.
113 string key = GetControllerKey(routeData, controllerName);
114 if (key == null)
115 {
116 throw new HttpResponseException(HttpStatusCode.NotFound);
117 }
118
119 HttpControllerDescriptor controllerDescriptor;
120 if (_controllers.Value.TryGetValue(key, out controllerDescriptor))
121 {
122 return controllerDescriptor;
123 }
124 else if (_duplicates.Contains(key))
125 {
126 throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Multiple controllers were found that match this request."));
127 }
128 else
129 {
130 throw new HttpResponseException(HttpStatusCode.NotFound);
131 }
132 }
133 public IDictionarystring, HttpControllerDescriptor> GetControllerMapping()
134 {
135 return _controllers.Value;
136 }
137 }
View Code
第三个文件:App_Start/WebApiConfig,不再使用默认的路由模板配置,而使用自定义实现
1 public static class WebApiConfig
2 {
3 public static void Register(HttpConfiguration config)
4 {
5 //// Web API 路由
6 //config.MapHttpAttributeRoutes();
7 //config.Routes.MapHttpRoute(
8 // name: "DefaultApi",
9 // routeTemplate: "api/{controller}/{id}",
10 // defaults: new { id = RouteParameter.Optional }
11 //);
12
13 // Web API 支持namespaces
14 config.Services.Replace(typeof(IHttpControllerSelector), new NamespaceHttpControllerSelector(config));
15 config.Routes.MapHttpRoute(
16 name: "DefaultApi",
17 routeTemplate: "api/{controller}/{action}/{id}",
18 defaults: new { id = RouteParameter.Optional },
19 namespaces: new string[] { "xxxx.Controllers" }
20 );
21 }
22 }
View Code
鉴于上述代码的基础来源于网络多处,虽然由本人重新设计后实现了namespaces参数的支持,但不能算原创本人只能算是搬运工,即:取于网络,还于网络,如果能帮到您、我便很开心了。
让WebApi路由配置像Mvc一样支持namespaces参数
标签:项目 第一个 hash reac option ping mat cti contain
原文地址:https://www.cnblogs.com/Jkinbor/p/13172383.html