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<string, object> 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("”);
  values.Add(");

  ");
  routeData.Values.Add(");

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

  最终结果是weather/021/3

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

  最后才返回一个HttpVirtualPathData对象。

ASP.NET Web API 框架研究 ASP.NET Web API 路由的更多相关文章

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

    ASP.NET Web API 如果采用Web Host方式来寄宿,在请求进入Web API 消息处理管道之前,就会用ASP.NET 自身的路由系统根据注册的路由表,解析出当前请求的HttpContr ...

  2. ASP.NET Web API 框架研究 Web Host模式路由及将请求转出到消息处理管道

    Web Host 模式下的路由本质上还是通过ASP.NET 路由系统来进行路由的,只是通过继承和组合的方式对ASP.NET路由系统的内部的类进行了一些封装,产生自己专用一套类结构,功能逻辑基本都是一样 ...

  3. ASP.NET Web API 框架研究 Action方法介绍

    在根据请求解析出匹配的Controller类型并创建实例后,要在该Controller类型中的众多Action方法中选择与请求匹配的那一个,并执行,然后返回响应. Action方法,其元数据,主要包括 ...

  4. ASP.NET Web API 框架研究 Controller实例的销毁

    我们知道项目中创建的Controller,如ProductController都继承自ApiController抽象类,其又实现了接口IDisposable,所以,框架中自动调用Dispose方法来释 ...

  5. ASP.NET Web API 框架研究 服务容器 ServicesContainer

    ServicesContainer是一个服务的容器,可以理解为—个轻量级的IoC容器,其维护着一个服务接口类型与服务实例之间的映射关系,可以根据服务接口类型获取对应的服务实例.构成ASP.NET We ...

  6. ASP.NET Web API 框架研究 核心的消息处理管道

    ASP.NET Web API 的核心框架是一个由一组HttpMessageHandler有序组成的双工消息处理管道:寄宿监听到请求接受后,把消息传入该管道经过所有HttpMessageHandler ...

  7. ASP.NET Web API 框架研究 IoC容器 DependencyResolver

    一.概念 1.IoC(Inversion of Control),控制反转 即将依赖对象的创建和维护交给一个外部容器来负责,而不是应用本身.如,在类型A中需要使用类型B的实例,而B的实例的创建不是由A ...

  8. ASP.NET Web API 框架研究 Self Host模式下的消息处理管道

    Self Host模式下的ASP.NET Web API与WCF非常相似,都可以寄宿在任意类型的托管应用程序中,宿主可以是Windows Form .WPF.控制台应用以及Windows Servic ...

  9. ASP.NET Web API 框架研究 Web Host模式下的消息处理管道

    寄宿的作用是开启一个进程为Web API提供一个运行环境以解决持续监听.请求监听和响应回复,即将接收到的请求转换成HttpRequestMessage对象传入管道,并将管道生成并经过处理后的HttpR ...

随机推荐

  1. dbc file

    DBC文件是用来描述CAN网络通信信号的一种格式文件.它可以用来监测与分析CAN网络上的报文数据,也可以用来模拟某个CAN节点.(DBC file is a format file used to d ...

  2. 通过flask中的Response返回json数据

    使用flask的过程中,发现有时需要生成一个Response并返回.网上查了查,看了看源码,找到了两种办法: from flask import Response, json Response(jso ...

  3. 【搜索】Dungeon Master

    Description You are trapped in a 3D dungeon and need to find the quickest way out! The dungeon is co ...

  4. 20155312 2016-2017-2《Java程序设计》课程总结

    20155312 2016-2017-2<Java程序设计>课程总结 每周作业链接汇总 预备作业1:你期望的师生关系是什么? 预备作业2:做中学learning by doing个人感想 ...

  5. 导入CA证书报错 keytool error: java.lang.Exception: Input not an X.509 certificate

    导入CA证书报错: keytool error: java.lang.Exception: Input not an X.509 certificate 如果你的CA证书是如下格式的: -----BE ...

  6. ajax +jsp+iframe无刷新上传文件[转]

    http://hi.baidu.com/zj360202/blog/item/f23e3711f929c774cb80c475.html ajax jsp 无刷新上传文件 2009-10-26 16: ...

  7. hashable/iterable与orderable

    ################ # hashable协议 # ################ # 一个对象能被称为hashable,它必须实现__hash__与_eq__方法: >>& ...

  8. 解决maltab的中文和英文字体问题,中文乱码

    用比较好看的编程字体,偏偏不显示中文,用支持中文的字体,英文不是等宽的,非常难看. 最近在网上找这方面的解决方法,发现解决问题的方法还是有的. 其实这个问题的原因就是系统自带的等宽字体,不支持中文,解 ...

  9. Linux 的虚拟文件系统(强烈推荐)

    1 引言 Linux 中允许众多不同的文件系统共存,如 ext2, ext3, vfat 等.通过使用同一套文件 I/O 系统 调用即可对 Linux 中的任意文件进行操作而无需考虑其所在的具体文件系 ...

  10. 利用url传多个参数

    刚开始接触jsp,比较偏向于用button标签的onclick方法进行页面的跳转.但是关于页面跳转的各种问题真是叫人头大,以下记录,仅仅为自己以后查看. Qone 用url传参的时候遇到中文怎么办 编 ...