• ASP.NET Web API 框架研究 ASP.NET Web API 路由


      ASP.NET Web API 核心框架是一个独立的、抽象的消息处理管道,ASP.NET Web API有自己独立的路由系统,是消息处理管道的组成部分,其与ASP.NET路由系统有类似的设计,都能找到对应的类,虽然有一定区别,基本都是面向接口的,而且命名都以Http开始的,但是,其主要辑基本都一样。看这篇之前先看上一篇《ASP.NET Web API 框架研究 ASP.NET 路由》

    一、涉及的类及源码分析

      涉及的主要类型都在程序集“System.Net.Http.dll”和“System.Web.Http.dll”中,类及主要成员和相互关系如下图:

     

    1、HttpRequestMessage  HttpResponseMessage  HttpContent

      ASP.NET中请求和响应分别用HttpRequest和HttpResponse表示,直接从当前请求上下文HttpContext获取,而ASP.NET Web API通过HttpRequestMessage和HttpResponseMessage来表示,与HttpContext无关,且他们都有一个HttpContent属性,用来表示请求和响应报文的主体内容;这几个类在程序集“System.Net.Http.dll”中。

      public class HttpRequestMessage : IDisposable
      {
        //多个重载的构造函数,注意参数HttpMethod 和requestUri
        public HttpRequestMessage();
        public HttpRequestMessage(HttpMethod method, Uri requestUri);
        public HttpRequestMessage(HttpMethod method, string requestUri);

        //请求主对象体内容
        public HttpContent Content { get; set; }
        //请求报头集合
        public HttpRequestHeaders Headers { get; }
        //起始行的请求方法,默认值GET方法
        public HttpMethod Method { get; set; }
        //起始行的请求URi
        public Uri RequestUri { get; set; }
        //起始行的请求Http协议版本,默认值HTTP 1.1
        public Version Version { get; set; }

        //字典属性,可以将任意对象附加进来,主要在请求过程不同阶段传递数据
        public IDictionary<stringobject> Properties { get; }

        //Dispose模式,销毁非托管资源

        public void Dispose()

        {

          this.Dispose(true);

          GC.SuppressFinalize(this);

        }
        //子类逻辑可以重写资源回收
        protected virtual void Dispose(bool disposing)

        {      

          if (!disposing || this.disposed)
            return;
          this.disposed = true;
          if (this.content == null)
            return;
          this.content.Dispose();

        }

        public override string ToString();
      }

      //无字典属性Properties

      public class HttpResponseMessage : IDisposable
      {
        //构造函数
        public HttpResponseMessage();
        public HttpResponseMessage(HttpStatusCode statusCode);

        //响应主体内容
        public HttpContent Content { get; set; }
        //响应报头头集合
        public HttpResponseHeaders Headers { get; }
        //响应是否成功,响应码200-299之间
        public bool IsSuccessStatusCode { get; }
        //起始行的状态文字描述
        public string ReasonPhrase { get; set; }
        //对应的请求消息
        public HttpRequestMessage RequestMessage { get; set; }
        //起始行的响应码
        public HttpStatusCode StatusCode { get; set; }
        //起始行的HTTP版本
        public Version Version { get; set; }

        public void Dispose();
        public HttpResponseMessage EnsureSuccessStatusCode();
        public override string ToString();
        protected virtual void Dispose(bool disposing);
      }

      public abstract class HttpContent : IDisposable
      {
        protected HttpContent();

        //跟主体内容相关的请求或响应头集合
        public HttpContentHeaders Headers { get; }
        //将HTTP内容序列化为字节流
        public Task CopyToAsync(Stream stream);
        //将HTTP内容序列化到内存缓冲区
        public Task LoadIntoBufferAsync();
        //将HTTP内容序列化到字节数组
        public Task<byte[]> ReadAsByteArrayAsync();
        public Task<Stream> ReadAsStreamAsync();
        //将HTTP内容序列化为字符串
        public Task<string> ReadAsStringAsync();
        public void Dispose();
        protected virtual void Dispose(bool disposing);
      }

      HttpContent是抽象类,默认有很多子类继承它,表示不同的主体内容表示形式,如ByteArrayContent就是字节数组表示的请求或响应主体内容,另外,还有如ObjectContent,将对象的序列化结果作为请求或响应主体内容,HttpMessageContent将整个报文内容(主体、报头集合、主体内容)作为主体内容,这种方式的媒体类型即Content-Type为“application/http,msgtype=request”或“application/http,msgtype=response”

      

      

    2、HttpRoute IHttpRoute

      HttpRoute为ASP.NET Web API路由对象,所有的路由对象都继承IHttpRoute接口,不像ASP.NET路由系统中路由对象Route都继承抽象类RouteBase,而且成员还有一定区别。 

      public interface IHttpRoute
      {

        //路由模板,ASP.NET路由中是URL
        string RouteTemplate { get; }

        //默认值

        IDictionary<string, object> Defaults { get; }

        //约束
        IDictionary<string, object> Constraints { get; }

        //附加变量
        IDictionary<string, object> DataTokens { get; }

        //ASP.NET Web API核心类型,消息处理管道就是由一组HttpMessageHandler,ASP.NET路由中是IRouteHandler
        HttpMessageHandler Handler { get; }

        //两大功能之一,解析请求根据路由模板进行匹配获得路由数据

        IHttpRouteData GetRouteData(string virtualPathRoot, HttpRequestMessage request);

        //两大功能之一,根据提供的路由变量,根据路由模板生成URL,供应用使用
        IHttpVirtualPathData GetVirtualPath(HttpRequestMessage request, IDictionary<string, object> values);
      }

      以下是HttpRoute主要源代码,可作为写代码参考 

      public class HttpRoute : IHttpRoute
      {
        public static readonly string HttpRouteKey = "httproute";
        internal const string RoutingContextKey = "MS_RoutingContext";

        private string _routeTemplate;
        private HttpRouteValueDictionary _defaults;
        private HttpRouteValueDictionary _constraints;
        private HttpRouteValueDictionary _dataTokens;

        public HttpRoute()
          : this(routeTemplate: null, defaults: null, constraints: null, dataTokens: null, handler: null, parsedRoute: null)
        {
        }
        //省略N个重载构造函数
        internal HttpRoute(string routeTemplate, HttpRouteValueDictionary defaults, HttpRouteValueDictionary constraints, HttpRouteValueDictionary dataTokens,       HttpMessageHandler handler, HttpParsedRoute parsedRoute)
        {
          _routeTemplate = routeTemplate == null ? String.Empty : routeTemplate;
          _defaults = defaults ?? new HttpRouteValueDictionary();
          _constraints = constraints ?? new HttpRouteValueDictionary();
          _dataTokens = dataTokens ?? new HttpRouteValueDictionary();
          Handler = handler;

          if (parsedRoute == null)
          {
            ParsedRoute = RouteParser.Parse(routeTemplate);
          }
          else
          {
            ParsedRoute = parsedRoute;
          }
        }

        public IDictionary<string, object> Defaults
        {
          get { return _defaults; }
        }
        //省略Constraints,DataTokens,RouteTemplate

        public HttpMessageHandler Handler { get; private set; }

        internal HttpParsedRoute ParsedRoute { get; private set; }

        //匹配路由
        public virtual IHttpRouteData GetRouteData(string virtualPathRoot, HttpRequestMessage request)
        {
          if (virtualPathRoot == null)
          {
            throw Error.ArgumentNull("virtualPathRoot");
          }

          if (request == null)
          {
            throw Error.ArgumentNull("request");
          }

          RoutingContext context = GetOrCreateRoutingContext(virtualPathRoot, request);
          if (!context.IsValid)
          {
            return null;
          }

          //1.匹配模板路由,调用ParsedRoute.Match

          HttpRouteValueDictionary values = ParsedRoute.Match(context, _defaults);
          if (values == null)
          {
            // 返回空说明没有匹配
            return null;
          }

          // 验证所有约束
          if (!ProcessConstraints(request, values, HttpRouteDirection.UriResolution))
          {
            return null;
          }

          return new HttpRouteData(this, values);
        }

        private static RoutingContext GetOrCreateRoutingContext(string virtualPathRoot, HttpRequestMessage request)
        {
          RoutingContext context;
          if (!request.Properties.TryGetValue<RoutingContext>(RoutingContextKey, out context))
          {
            context = CreateRoutingContext(virtualPathRoot, request);

            //将路由上下文数据放到请求的字段属性中,包含virtualPathRoot
            request.Properties[RoutingContextKey] = context;
          }

          return context;
        }

        //生成URL
        public virtual IHttpVirtualPathData GetVirtualPath(HttpRequestMessage request, IDictionary<string, object> values)
        {
          if (request == null)
          {
            throw Error.ArgumentNull("request");
          }

          //参数中路由变量不包含httproute的话,直接返回null,一个隐含条件参数字典中一定要包含httproute的key

          if (values != null && !values.Keys.Contains(HttpRouteKey, StringComparer.OrdinalIgnoreCase))
          {
            return null;
          }
          // 验证包含httproute后,就把其重路由变量字典中去除掉
          var newValues = GetRouteDictionaryWithoutHttpRouteKey(values);

          IHttpRouteData routeData = request.GetRouteData();
          IDictionary<string, object> requestValues = routeData == null ? null : routeData.Values;

          //通过传进来的参数,以及HttpMessageRequest中的路由数据,以及HttpRoute本身的默认值,按这个优先级,解析生成URL

          BoundRouteTemplate result = ParsedRoute.Bind(requestValues, newValues, _defaults, _constraints);
          if (result == null)
          {
            return null;
          }

          //验证路由匹配约束
          if (!ProcessConstraints(request, result.Values, HttpRouteDirection.UriGeneration))
          {
            return null;
          }

          return new HttpVirtualPathData(this, result.BoundTemplate);
        }

        //从路由变量表里去除httproute变量

        private static IDictionary<string, object> GetRouteDictionaryWithoutHttpRouteKey(IDictionary<string, object> routeValues)
        {
          var newRouteValues = new HttpRouteValueDictionary();
          if (routeValues != null)
          {
            foreach (var routeValue in routeValues)
            {
              if (!String.Equals(routeValue.Key, HttpRouteKey, StringComparison.OrdinalIgnoreCase))
              {
                newRouteValues.Add(routeValue.Key, routeValue.Value);
              }
            }
          }
          return newRouteValues;
        }

        //检查单个约束字典中的约束,约束是针对某个变量的

        protected virtual bool ProcessConstraint(HttpRequestMessage request, object constraint, string parameterName, HttpRouteValueDictionary values, HttpRouteDirection       routeDirection)
        {

          //先检查是不是继承自IHttpRouteConstraint约束类型
          IHttpRouteConstraint customConstraint = constraint as IHttpRouteConstraint;
          if (customConstraint != null)
          {

            //是的话就调用自身的Match方法验证
            return customConstraint.Match(request, this, parameterName, values, routeDirection);
          }

          // 没有自定义约束就用正则表达式字符串匹配
          string constraintsRule = constraint as string;
          if (constraintsRule == null)
          {
            throw Error.InvalidOperation(SRResources.Route_ValidationMustBeStringOrCustomConstraint, parameterName, RouteTemplate,               typeof(IHttpRouteConstraint).Name);
          }

          //验证正则表达式

          object parameterValue;
          values.TryGetValue(parameterName, out parameterValue);
          string parameterValueString = Convert.ToString(parameterValue, CultureInfo.InvariantCulture);
          string constraintsRegEx = "^(" + constraintsRule + ")$";
          return Regex.IsMatch(parameterValueString, constraintsRegEx, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
          }

        //检查约束字典中的每个约束

        private bool ProcessConstraints(HttpRequestMessage request, HttpRouteValueDictionary values, HttpRouteDirection routeDirection)
        {
          if (Constraints != null)
          {

            //遍历每个约束,全部通过了,才算通过
            foreach (KeyValuePair<string, object> constraintsItem in Constraints)
            {
              if (!ProcessConstraint(request, constraintsItem.Value, constraintsItem.Key, values, routeDirection))
              {
                return false;
              }
            }
          }

          return true;
        }

        internal static void ValidateConstraint(string routeTemplate, string name, object constraint)
        {
          if (constraint is IHttpRouteConstraint)
          {
            return;
          }

          if (constraint is string)
          {
            return;
          }

          throw CreateInvalidConstraintTypeException(routeTemplate, name);
        }

        private static Exception CreateInvalidConstraintTypeException(string routeTemplate, string name)
        {
          return Error.InvalidOperation(
            SRResources.Route_ValidationMustBeStringOrCustomConstraint,
            name,
            routeTemplate,
            typeof(IHttpRouteConstraint).FullName);
        }
      }

    3、HttpRouteData IHttpRouteData HttpRouteValueDictionary 

      HttpRouteData用来封装解析请求匹配路由模板并满足所有约束条件后的路由数据,其继承自IHttpRouteData ,与ASP.NET路由的RouteData相比成员更简单了

      public interface IHttpRouteData
      {

        //生成该数据的 IHttpRoute
        IHttpRoute Route { get; }

        //解析出来的路由变量

        IDictionary<string, object> Values { get; }
      }

      public class HttpRouteData : IHttpRouteData
      {
        private IHttpRoute _route;
        private IDictionary<string, object> _values;

        public HttpRouteData(IHttpRoute route)
          : this(route, new HttpRouteValueDictionary())
        {
        }

        public HttpRouteData(IHttpRoute route, HttpRouteValueDictionary values)
        {
          if (route == null)
          {
            throw Error.ArgumentNull("route");
          }

          if (values == null)
          {
            throw Error.ArgumentNull("values");
          }

          _route = route;
          _values = values;
        }

        public IHttpRoute Route
        {
          get { return _route; }
        }

        public IDictionary<string, object> Values
        {
          get { return _values; }
        }
      }

      

      public class HttpRouteValueDictionary : Dictionary<string, object>
      {
        public HttpRouteValueDictionary()
          : base(StringComparer.OrdinalIgnoreCase)
        {
        }

        public HttpRouteValueDictionary(IDictionary<string, object> dictionary)
          : base(StringComparer.OrdinalIgnoreCase)
        {
          if (dictionary != null)
          {
            foreach (KeyValuePair<string, object> current in dictionary)
            {
              Add(current.Key, current.Value);
            }
          }
        }

        public HttpRouteValueDictionary(object values)
          : base(StringComparer.OrdinalIgnoreCase)
        {
          IDictionary<string, object> valuesAsDictionary = values as IDictionary<string, object>;
          if (valuesAsDictionary != null)
          {
            foreach (KeyValuePair<string, object> current in valuesAsDictionary)
            {
              Add(current.Key, current.Value);
            }
          }
          else if (values != null)
          {
            foreach (PropertyHelper property in PropertyHelper.GetProperties(values))
            {
              Add(property.Name, property.GetValue(values));
            }
          }
        }
      }

    4、HttpVirtualPathData IHttpVirtualPathData 

        调用GetVirtaulPath方法的返回值,比ASP.NET 路由中的VirtualPathData少个DataTokens,

      public interface IHttpVirtualPathData
      {
        IHttpRoute Route { get; }

        //生成的URL

        string VirtualPath { get; set; }
      }

      

      public class HttpVirtualPathData : IHttpVirtualPathData
      {
        private string _virtualPath;

        public HttpVirtualPathData(IHttpRoute route, string virtualPath)
        {
          if (route == null)
          {
            throw Error.ArgumentNull("route");
          }

          if (virtualPath == null)
          {
            throw Error.ArgumentNull("virtualPath");
          }

          Route = route;
          VirtualPath = virtualPath;
        }

        public IHttpRoute Route { get; private set; }

        public string VirtualPath
        {
          get { return _virtualPath; }
          set
          {
            if (value == null)
            {
              throw Error.PropertyNull();
            }
            _virtualPath = value;
          }
        }
      }

    5、 IHttpRouteConstraint HttpRouteDirection

      除了正则表达式的约束外,可以自定义针对某个路由变量的约束,对应ASP.NET路由中的 IRouteConstraint

      解析出路由数据后,还要验证请求是否满足所有约束条件

      public interface IHttpRouteConstraint
      {
        /// </summary>
        /// <param name="request">被验证的请求</param>
        /// <param name="route">约束本身所在的路由对象</param>
        /// <param name="parameterName">由于约束通常是针对某个路由变量的,这个就是路由变量</param>
        /// <param name="values">是之前通过URL模式匹配得到的所有路由变量值</param>
        /// <param name="routeDirection">路由方向,这个约束可以用在路由匹配上,也可以用在生成URL上</param>
        bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection);
      } 

      //路由方向,一个进,一个出,用在不同场景

      public enum HttpRouteDirection
      {

        //利用路由模板匹配请求进而得到路由数据
        UriResolution = 0,

        //根据路由规则和路由变量生成URL
        UriGeneration
      }

      ASP.NET WEB API框架定义了很多实现,如BoolRouteConstraint 验证解析出来的某个路由变量是否是一个bool类型,又如HttpMethodConstraint约束请求方法

      public class BoolRouteConstraint : IHttpRouteConstraint
      {
        public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection)
        {
          if (parameterName == null)
          {
            throw Error.ArgumentNull("parameterName");
          }

          if (values == null)
          {
            throw Error.ArgumentNull("values");
          }

          object value;
          if (values.TryGetValue(parameterName, out value) && value != null)
          {
            if (value is bool)
            {
              return true;
            }

            bool result;
            string valueString = Convert.ToString(value, CultureInfo.InvariantCulture);
            return Boolean.TryParse(valueString, out result);
          }
          return false;
        }
      }

      public class HttpMethodConstraint : IHttpRouteConstraint
      {
        public HttpMethodConstraint(params HttpMethod[] allowedMethods)
        {
          if (allowedMethods == null)
          {
            throw Error.ArgumentNull("allowedMethods");
          }

          AllowedMethods = new Collection<HttpMethod>(allowedMethods);
        }

        public Collection<HttpMethod> AllowedMethods { get; private set; }

        protected virtual bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection       routeDirection)
        {
          if (request == null)
          {
            throw Error.ArgumentNull("request");
          }

          if (route == null)
          {
            throw Error.ArgumentNull("route");
          }

          if (parameterName == null)
          {
            throw Error.ArgumentNull("parameterName");
          }

          if (values == null)
          {
            throw Error.ArgumentNull("values");
          }

          switch (routeDirection)
          {
            case HttpRouteDirection.UriResolution:
              return AllowedMethods.Contains(request.Method);

            case HttpRouteDirection.UriGeneration:
              HttpMethod constraint;
              if (!values.TryGetValue(parameterName, out constraint))
              {
                return true;
              }

              return AllowedMethods.Contains(constraint);

            default:
              throw Error.InvalidEnumArgument(String.Empty, (int)routeDirection, typeof(HttpRouteDirection));
          }
        }

        bool IHttpRouteConstraint.Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection       routeDirection)
        {
          return Match(request, route, parameterName, values, routeDirection);
        }
      }

    6、HttpRouteCollection

       同ASP.NET路由中的全局路由表类型RouteCollection,但是创建路由是方法CreateRoute方法,同时也有对应GetVirtualPath和GetRouteData方法,逻辑差不多按顺序调用每个HttpRoute对象的同名方法,匹配或生成成功就马上返回,否则返回NULL,见下边源码

      public class HttpRouteCollection : ICollection<IHttpRoute>, IDisposable
      {
        private static readonly Uri _referenceBaseAddress = new Uri("http://localhost");

        private readonly string _virtualPathRoot;
        private readonly List<IHttpRoute> _collection = new List<IHttpRoute>();
        private readonly IDictionary<string, IHttpRoute> _dictionary = new Dictionary<string, IHttpRoute>(StringComparer.OrdinalIgnoreCase);
        private bool _disposed;

        public HttpRouteCollection()
        : this("/")
        {
        }

        public HttpRouteCollection(string virtualPathRoot)
        {
          if (virtualPathRoot == null)
          {
            throw Error.ArgumentNull("virtualPathRoot");
          }

          Uri address = new Uri(_referenceBaseAddress, virtualPathRoot);
          _virtualPathRoot = "/" + address.GetComponents(UriComponents.Path, UriFormat.Unescaped);
        }

         //按顺序遍历路由集合中的每个路由对应的方法,来获取路由解析数据

        public virtual IHttpRouteData GetRouteData(HttpRequestMessage request)
        {
          if (request == null)
          {
            throw Error.ArgumentNull("request");
          }

          //循环遍历每个HttpRoute

          for (int i = 0; i < _collection.Count; i++)
          {

            //virtualPathRoot 可能存放在HttpRequestMessage中的字典属性中,如果存在就直接使用,否则使用默认的,virtualPathRoot 的影响见后边路由解析
            string virtualPathRoot = GetVirtualPathRoot(request.GetRequestContext());
            IHttpRouteData routeData = _collection[i].GetRouteData(virtualPathRoot, request);
            if (routeData != null)
            {
              return routeData;
            }
          }

          return null;
        }

        //利用某个路由名称(name)指定的路由,以及路由字典数据,以及请求HttpRequestMessage ,生成URL

        public virtual IHttpVirtualPathData GetVirtualPath(HttpRequestMessage request, string name, IDictionary<string, object> values)
        {
          if (request == null)
          {
            throw Error.ArgumentNull("request");
          }

          if (name == null)
          {
            throw Error.ArgumentNull("name");
          }

          //根据路由名称从路由表中获取某个路由

          IHttpRoute route;
          if (!_dictionary.TryGetValue(name, out route))
          {
            throw Error.Argument("name", SRResources.RouteCollection_NameNotFound, name);
          }

          //再用该路由调用GetVirtualPath方法获得HttpVirtualPathData
          IHttpVirtualPathData virtualPath = route.GetVirtualPath(request, values);
          if (virtualPath == null)
          {
            return null;
          }

          //virtualPathRoot 可能存放在HttpRequestMessage中的字典属性中,如果存在就直接使用,否则使用默认的

          string virtualPathRoot = GetVirtualPathRoot(request.GetRequestContext());
          if (!virtualPathRoot.EndsWith("/", StringComparison.Ordinal))
          {
            virtualPathRoot += "/";
          }

          //最终URL要不跟路径和解析出来的URL拼装起来
          return new HttpVirtualPathData(virtualPath.Route, virtualPathRoot + virtualPath.VirtualPath);
      }


      private string GetVirtualPathRoot(HttpRequestContext requestContext)
      {
        if (requestContext != null)
        {
          return requestContext.VirtualPathRoot ?? String.Empty;
        }

        return _virtualPathRoot;
      }


      public virtual IHttpRoute CreateRoute(string routeTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object>     dataTokens, HttpMessageHandler handler)
      {
        HttpRouteValueDictionary routeDefaults = new HttpRouteValueDictionary(defaults);
        HttpRouteValueDictionary routeConstraints = new HttpRouteValueDictionary(constraints);
        HttpRouteValueDictionary routeDataTokens = new HttpRouteValueDictionary(dataTokens);

        foreach (var constraint in routeConstraints)
        {
          ValidateConstraint(routeTemplate, constraint.Key, constraint.Value);
        }

        return new HttpRoute(routeTemplate, routeDefaults, routeConstraints, routeDataTokens, handler);
      }


      protected virtual void ValidateConstraint(string routeTemplate, string name, object constraint)
      {
        if (name == null)
        {
          throw Error.ArgumentNull("name");
        }

        if (constraint == null)
        {
          throw Error.ArgumentNull("constraint");
        }

        HttpRoute.ValidateConstraint(routeTemplate, name, constraint);
      }

      public virtual void Add(string name, IHttpRoute route)
      {
        if (name == null)
        {
          throw Error.ArgumentNull("name");
        }

        if (route == null)
        {
          throw Error.ArgumentNull("route");
        }

        _dictionary.Add(name, route);
        _collection.Add(route);
      }

      public virtual void Clear()

      {
        _dictionary.Clear();
        _collection.Clear();
      }

      public virtual bool ContainsKey(string name)
      {
        if (name == null)
        {
          throw Error.ArgumentNull("name");
        }

        return _dictionary.ContainsKey(name);
      }


      public virtual void Insert(int index, string name, IHttpRoute value)
      {
        if (name == null)
        {
          throw Error.ArgumentNull("name");
        }

        if (value == null)
        {
          throw Error.ArgumentNull("value");
        }

        if (_collection[index] != null)
        {
          _dictionary.Add(name, value);
          _collection.Insert(index, value);
        }
      }

      public void Dispose()
      {
        Dispose(true);
        GC.SuppressFinalize(this);
      }

      protected virtual void Dispose(bool disposing)
      {
        if (!_disposed)
        {
          if (disposing)
          {
            HashSet<IDisposable> handlers = new HashSet<IDisposable>();
            foreach (var route in this)
            {
              if (route.Handler != null)
              {
                handlers.Add(route.Handler);
              }
            }

            foreach (var handler in handlers)
            {
              handler.Dispose();
            }
          }

          _disposed = true;
        }
      }
    }

    7、HttpConfiguration HttpRouteCollection

      HttpConfiguration用来完成ASP.NET Web API的所有基本配置,是针对整个应用的全局配置,路由表就放在其中,是一个只读属性Routes,类型为HttpRouteCollection,不像ASP.NET路由表是放在类RouteTable的静态属性Routes里,另外,值得一提的是Properties,类似HttpRequestMessage同名字典属性,可以往里添加任何对象,这里可供全局应用使用;另外,我们注册和忽略路由定义了一个扩展方法来实现,间接调用路由表里方法,这个包含扩展方法的类就是HttpRouteCollectionExtensions,不像ASP.NET路由直接调用Routes方法。

      public class HttpConfiguration : IDisposable
      {
        private readonly HttpRouteCollection _routes;
        private readonly ConcurrentDictionary<object, object> _properties = new ConcurrentDictionary<object, object>();
        private readonly MediaTypeFormatterCollection _formatters;
        private readonly Collection<DelegatingHandler> _messageHandlers = new Collection<DelegatingHandler>();
        private readonly HttpFilterCollection _filters = new HttpFilterCollection();

        private IDependencyResolver _dependencyResolver = EmptyResolver.Instance;
        private Action<HttpConfiguration> _initializer = DefaultInitializer;
        private bool _initialized;

        private bool _disposed;

        public HttpRouteCollection Routes { get; }

        //...

      }

      public static class HttpRouteCollectionExtensions
      {

        //..省略N个重载,注册路由映射
        public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name, string routeTemplate, object defaults, object constraints, HttpMessageHandler       handler)
        {
          if (routes == null)
          {
            throw Error.ArgumentNull("routes");
          }

          HttpRouteValueDictionary defaultsDictionary = new HttpRouteValueDictionary(defaults);
          HttpRouteValueDictionary constraintsDictionary = new HttpRouteValueDictionary(constraints);

          //先创建建路由
          IHttpRoute route = routes.CreateRoute(routeTemplate, defaultsDictionary, constraintsDictionary, dataTokens: null, handler: handler);

          //再往路由表里添加路由
          routes.Add(name, route);
          return route;
        }


        public static IHttpRoute IgnoreRoute(this HttpRouteCollection routes, string routeName, string routeTemplate)
        {
          return IgnoreRoute(routes, routeName, routeTemplate, constraints: null);
        }

        //注册忽略路由
        public static IHttpRoute IgnoreRoute(this HttpRouteCollection routes, string routeName, string routeTemplate, object constraints)
        {
          if (routes == null)
          {
            throw new ArgumentNullException("routes");
          }
          if (routeName == null)
          {
            throw new ArgumentNullException("routeName");
          }
          if (routeTemplate == null)
          {
            throw new ArgumentNullException("routeTemplate");
          }

          IgnoreHttpRouteInternal route = new IgnoreHttpRouteInternal(routeTemplate, new HttpRouteValueDictionary(constraints), new StopRoutingHandler());
          routes.Add(routeName, route);
          return route;
        }

        private sealed class IgnoreHttpRouteInternal : HttpRoute
        {
          public IgnoreHttpRouteInternal(string routeTemplate, HttpRouteValueDictionary constraints, HttpMessageHandler handler)
          : base(routeTemplate, constraints: constraints, handler: handler, dataTokens: null, defaults: null)
          {
          }

          public override IHttpVirtualPathData GetVirtualPath(HttpRequestMessage request, IDictionary<string, object> values)
          {
            return null;
          }
        }
      }

    二、注册路由映射

      在全局配置对象HttpConfiguration的路由表Routes里添加一个HttpRoute对象,利用HttpRouteCollection(Routes的类型)的扩展方法MapHttpRoute来注册,如下

      

      其中要注意的是defaults为默认值,id设置成了RouteParameter.Optional意思可缺省,即如果请求URL没有提供对应Id,则解析结果就是没有Key为id的变量,如果是id设置成1,解析结果就是Key 为id,值为默认值1。

      具体内部逻辑可以看前边相关源码及注释

    三、路由匹配

      即调用HttpRoute对象的GetRouteData方法的逻辑,首先,请求URL要符合路由模板模式,模式匹配后把路由变量保存到字典对象中,其次,请求要满足所有约束,最后,通过前两者后生成HttpRouteData对象。

      另外,GetRouteData方法有个参数virtualPathRoot,默认值为“/” ,会影响解析结果,举个例子,

      路由模板:“books/{name}/{title}/{id}”

      请求URL:http://www.myhost/api/books/a/b/1

      virtualPathRoot参数值“/”和/api/,前者返回NULL,后者成功解析

      其实解析时候,先会把virtualPathRoot从请求URL中去除

      约束是针对某个路由变量的,其保存在HttpRoute的Constraints属性中,类型为IDictionary<string, object>,Key为某个路由变量,Value可以是某个HttpRouteConstraint对象或其字符串形式。

    四、生成URL

      即调用HttpRoute对象的GetVirtualPath方法的逻辑,即通过路由模板和指定的路由变量生成一个URL。

      首先,把路由模板中的变量占位符如{area}用路由变量值替换掉,路由变量值可以通过GetVirtualPath直接传入,也可以用HttpRequestMessage字典属性中的HttpRouteData值,或HttpRoute中定义的默认值,三者优先级从高到低,举个例子,

      路由模板:weather/{areacode}/{day}

      //values是参数传入,优先级最高,有限使用

      values.Add("httproute", true);

      values.Add("areacode", “021”);
      values.Add("days", 3);


      IHttpRouteData routeData = new HttpRouteData(route);
      routeData.Values.Add("areacode", "0512");
      routeData.Values.Add("days", 4);

      //往HttpRequestMessage字典属性中设置HttpRouteData,其中数据优先级比较低,不使用
      request.SetRouteData(routeData);
      pathData = route.GetVirtualPath(request, values);

      最终结果是weather/021/3

      当然前边获取到变量值后,如key为areacode的值021,还要检测其是否满足所有约束

      最后才返回一个HttpVirtualPathData对象。

  • 相关阅读:
    IDEA创建一个javaweb工程(在module中)以及配置Tomcat
    晨会复盘
    cnblog 笔记思路
    Mysql执行计划-extra
    Mysql执行计划分析-type(access_type)
    Mysql执行计划-selectType
    刻意训练
    MYSQL执行计划
    个人展望-程序员职业规划
    服务拆分原则
  • 原文地址:https://www.cnblogs.com/shawnhu/p/7988985.html
Copyright © 2020-2023  润新知