zoukankan      html  css  js  c++  java
  • [Web API] Web API 2 深入系列(5) 特性路由

    目录

    1. 特性路由注册
    
    2. 路由解析
    
        - 生成DataTokens
    
        - 选择HttpController
    
        - 选择Action
    

    特性路由的目的在于更好的提供restful架构的接口,最近好忙(懒),所以更新速度慢.

    特性路由注册

    • [Route(模板)] :定义特性路由模板

      • 普通变量

      a/b/{c}

      • 缺省变量

      a/b/{c=d}

      • 变量约束

      a/b/{c:int:range(10,20)}

      • 通配符

      a/b/{*c:datetime}

    [RoutePrefix("api/demo")] :定义路由前缀

    路由解析

    通过IRoutePrefix/IHttpRouteInfoProvider,我们可以直接注册路由,映射到具体的Controller和Action.

    当调用MapHttpAttributeRoutes方法时,WebAPI会创建1个唯一的RouteCollectionRoute作为IHttpRoute并添加到路由表中.

    MapHttpAttributeRoutes方法:

    public static void MapHttpAttributeRoutes(HttpConfiguration configuration, IInlineConstraintResolver constraintResolver, IDirectRouteProvider directRouteProvider)
    {
        RouteCollectionRoute aggregateRoute = new RouteCollectionRoute();
        configuration.Routes.Add("MS_attributerouteWebApi", (IHttpRoute) aggregateRoute);
        Action<HttpConfiguration> previousInitializer = configuration.Initializer;
        configuration.Initializer = (Action<HttpConfiguration>) (config =>
        {
            previousInitializer(config);
            aggregateRoute.EnsureInitialized((Func<IReadOnlyCollection<IHttpRoute>>) (() =>
            {
                subRoutes = new SubRouteCollection();
                AttributeRoutingMapper.AddRouteEntries(subRoutes, configuration, constraintResolver, directRouteProvider);
                return subRoutes;
            }));
        });
    }
    

    RouteCollectionRoute是特性路由的HttpRoute对象,既是一个IHttpRoute对象,又是一个IHttpRoute集合.并且其中核心方法为GetRouteData(IHttpRoute其他接口都返回为null),

    internal class RouteCollectionRoute : IHttpRoute, IReadOnlyCollection<IHttpRoute>, IEnumerable<IHttpRoute>, IEnumerable
    {
        public IHttpRouteData GetRouteData(string virtualPathRoot, HttpRequestMessage request)
        {
          List<IHttpRouteData> httpRouteDataList = new List<IHttpRouteData>();
          //调用内部的SubRoutes对象
          foreach (IHttpRoute subRoute in (IEnumerable<IHttpRoute>) this.SubRoutes)
          {
            IHttpRouteData routeData = subRoute.GetRouteData(virtualPathRoot, request);
            httpRouteDataList.Add(routeData);
          }
          return (IHttpRouteData) new RouteCollectionRoute.RouteCollectionRouteData((IHttpRoute) this, httpRouteDataList.ToArray());
        }
    }
    

    在该方法中,我们发现RouteCollectionRoute调用了内部所有的SubRoutes对象.

    而其内部的SubRoutes类型实际为SubRouteCollection类型

    internal class SubRouteCollection : IReadOnlyCollection<IHttpRoute>, IEnumerable<IHttpRoute>, IEnumerable
    {
        private readonly List<IHttpRoute> _routes = new List<IHttpRoute>();
        private readonly List<RouteEntry> _entries = new List<RouteEntry>();
        public IReadOnlyCollection<RouteEntry> Entries{get;}
    }
    

    而SubRoutes的创建是在MapHttpAttributeRoutes方法定义,实际调用是在HttpServer的Send方法初始化的.

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        this.EnsureInitialized();
        //...
    }
    

    首先验证一下我们的特性路由注册位置(定义1个扩展方法)

    public static class RouteCollectionExt
    {
        public static IEnumerable<IHttpRoute> GetSubRoutes(this HttpRouteCollection routes)
        {
            var route = routes["MS_attributerouteWebApi"];
            var prop = route.GetType().GetProperty("SubRoutes", BindingFlags.Instance | BindingFlags.NonPublic);
            var subRoutes = prop.GetValue(route) as IEnumerable<IHttpRoute>;
            return subRoutes;
        }
    }
    

    生成DataTokens

    DataTokens这次发挥了一定的作用,同时也告诉我们该如何使用它. (在第1节中,我觉得DataTokens是个冗余设计)

    先看下DataTokens上有哪些东西.

    private static void ShowSubRoutesTokens(HttpRouteCollection routes)
    {
        foreach (var subRoute in routes.GetSubRoutes())
        {
            Console.WriteLine(subRoute.RouteTemplate);
            foreach (var dataToken in subRoute.DataTokens)
            {
                Console.WriteLine("{0,-12}{1}", dataToken.Key, dataToken.Value);
            }
            Console.WriteLine();
        }
    
    }
    

    截图:

    对于DataTokens的actions和precedence的属性,在DirectRouteBuilder的Build方法中实现

    其中actions表示该路由模板对应的actiondescription(在特性路由中,会为每个controller创建独立的一份子路由.)

    而precedence表示匹配的优先级,对于有约束的优先级高于无优先级.(约束分为常量,变量,通配符)

    在前2节中,我们讲了如何选择Action以及Controller.
    实际上,如果使用特性路由.选择的机制又有些变化.

    选择HttpController

    public virtual HttpControllerDescriptor SelectController(HttpRequestMessage request)
    {
        IHttpRouteData routeData = request.GetRouteData();
        if (routeData != null)
        {
            //在GetDirectRouteController内获取了特性路由对应的Controller,同时要求匹配的所有特性路由对应的Controller为同一个
            HttpControllerDescriptor directRouteController = DefaultHttpControllerSelector.GetDirectRouteController(routeData);
            return directRouteController;
        }
        //普通路由方式
        string controllerName = this.GetControllerName(request);
        //...
    }
    

    选择Action

    public HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
    {
        var matchingActions = this.FindMatchingActions(controllerContext, false);
        //...
    }
    
    private List<ApiControllerActionSelector.CandidateActionWithParams> FindMatchingActions(HttpControllerContext controllerContext, bool ignoreVerbs = false)
    {
        //此处做特性路由判断
        IEnumerable<IHttpRouteData> subRoutes = controllerContext.RouteData.GetSubRoutes();
        return subRoutes == null ? 普通路由 : 特性路由;
    }
    

    备注:
    - 如果我们为特性路由指定了Name,则会自动创建一个IHttpRoute绑定到RouteCollection上.(这步是在HttpConfiguration初始化中最后做判断完成的)

    - 文章中的代码并非完整WebAPI代码,一般是经过自己精简后的.
    
    - 本篇内容使用MarkDown语法编辑
    

    首发地址:http://neverc.cnblogs.com/p/5975086.html

  • 相关阅读:
    SwiftyUserDefaults-封装系统本地化的框架推荐
    转:AFNetworking 与 UIKit+AFNetworking 详解
    转:KVC/KVO原理详解及编程指南
    转:NSString什么时候用copy,什么时候用strong
    代码重构原则
    转:【iOS开发每日小笔记(十一)】iOS8更新留下的“坑” NSAttributedString设置下划线 NSUnderlineStyleAttributeName 属性必须为NSNumber
    转:iOS程序main函数之前发生了什么
    转:iOS 屏幕适配,autoResizing autoLayout和sizeClass图文详解
    Apple macOS Mojave Intel Graphics Driver组件任意代码执行漏洞
    巧用"记事本"让病毒无效运行
  • 原文地址:https://www.cnblogs.com/neverc/p/5975086.html
Copyright © 2011-2022 走看看