zoukankan      html  css  js  c++  java
  • 【以前技术归档】让WebApi路由配置像Mvc一样支持namespaces参数

    很多年前就知道我软的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 { namespaces = 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 = "namespaces";
      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 NamespaceHttpControllerSelector(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                 // 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<T>(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 < namespaces.Length; 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 = GetRouteVariable<string>(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 IDictionary<string, 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参数的支持,但不能算原创本人只能算是搬运工,即:取于网络,还于网络,若能帮到您、我便很开心了。

    特别注意:在Asp.Net Web API项目里不要创建名称相同的Controller文件,如A文件夹里有TestController,B文件夹里也有TestController,这种操作是不被允许的

  • 相关阅读:
    spring学习(一)IOC&AOP
    MongoDB 写入数据的安全性
    MongoDB MapReduce
    MongoDB 原子操作
    MongoDB 文档间的关系
    MongoDB Java
    MongoDB 持久化
    MongoDB 聚合函数 aggregate
    MongoDB 索引
    MongoDB 文档操作
  • 原文地址:https://www.cnblogs.com/Jkinbor/p/13172383.html
Copyright © 2011-2022 走看看