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

时间:2022-09-22 14:59:45

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

  简单的概括起来,Web Host模式下的ASP.NET Web API在Web应用中注册的路由(HostedHttpRoute)最终会转换成ASP.NET 全局路由表中的Route(HttpWebRoute),ASP.NET路由系统会对请求进行匹配,把匹配的路由变量数据存放到RequestContext中,再通过UrlRoutingModule从匹配的Route(从RequestContext中获取,见ASP.NET路由部分代码)中获取IRouteHandler,即HttpControllerRouteHandler,其提供的HttpControllerHandler最终会创建出消息处理管道,即触发HttpServer,消息处理管道的第一个消息处理器。

一、涉及的类及源码分析 

  Web Host 模式下的类基本都在System.Web.Http.WebHost程序集中,相比Web API的接口基本都在System.Web.Http程序集中,包括GlobalConfiguration和HttpConfiguration类,主要类和成员如下图:

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

  简要说明:

  在静态类GlobalConfiguration中创建了HttpConfiguration对象,其构造参数默认指定为HostedHttpRouteCollection,而该集合的构造函数指定为RouteCollection类型的ASP.NET 路由系统全局路由表,通过组合方式对其进行了封装,将其作为HostedHttpRouteCollection的一个属性,用来存放ASP.NET 路由中的Route对象,路由注册时加入路由表中的路由对象HostedHttpRoute最终都要转换成Route对象。

  HttpConfiguration config = new HttpConfiguration(new HostedHttpRouteCollection(RouteTable.Routes));

  而HostedHttpRouteCollection继承自HttpRouteCollection,在其重写的CreateRoute方法中,创建的是一个HostedHttpRoute路由对象

  public override IHttpRoute CreateRoute(string uriTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object>     dataTokens,   HttpMessageHandler handler)

  {

    //...

    return new HostedHttpRoute(uriTemplate, defaults, constraints, dataTokens, handler);

  }

  而在创建HostedHttpRoute对象时候,在构造函数中,直接创建HttpWebRoute对象,并直接指定IRouteHandler为HttpControllerRouteHandler

  OriginalRoute = new HttpWebRoute(uriTemplate, routeDefaults, routeConstraints, routeDataTokens, HttpControllerRouteHandler.Instance, this);

  而HttpWebRoute直接继承自Route,所以本质上还是利用了ASP.NET路由的功能。

  另外,HttpControllerRouteHandler其方法GetHttpHandler默认返回的是HttpControllerHandler,也是直接指定

  protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
  {
    return new HttpControllerHandler(requestContext.RouteData);
  }

  在其构造函数中,默认传入了GlobalConfiguration.DefaultServer,其在GlobalConfiguration中指定的是HttpServer,其是ASP.NET Web API 消息处理管道的第一个处理器(HttpMessageHandler)

  public HttpControllerHandler(RouteData routeData)
  : this(routeData, GlobalConfiguration.DefaultServer)
  {
  }

  HttpControllerHandler是一个HttpHandler

   public class HttpControllerHandler : HttpTaskAsyncHandler

   public abstract class HttpTaskAsyncHandler : IHttpAsyncHandler, IHttpHandler

   在其ProcessRequestAsync方法中通过以下语句将请求转接入消息处理管道中。

  response = await _server.SendAsync(request, cancellationToken)

1、HttpWebRoute 

  HttpWebRoute 类直接继承ASP.NET路由中的Route,重写了主要方法GetRouteData、GetVirtualPath和ProcessConstraint,其主要功能还是调用基类的同名方法(如base.GetRouteData),另外,还有个HttpRoute属性,其是创建它的HostedHttpRoute对象,简要说明中已有描述,后边也会再次指出,约束检查还是用IHttpRouteConstraint。

  可知,HttpWebRoute 是Web Host模式下ASP.NET Web API实际执行路由功能的对象,而其实际又使用(继承)了ASP.NET路由系统的Route对象来实现的,实现了ASP.NET Web API路由功能和ASP.NET路由的链接。另外,HttpWebRoute对象是通过HostedHttpRoute对象创建的。

  总结下来它是对ASP.NET路由系统中Route对象封装。

  //继承ASP.NET路由的Route

  internal class HttpWebRoute : Route
  {
    internal const string HttpRouteKey = "httproute";

    public HttpWebRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler,       IHttpRoute httpRoute)
      : base(url, defaults, constraints, dataTokens, routeHandler)
    {
      if (httpRoute == null)
      {
        throw Error.ArgumentNull("httpRoute");
      }

      HttpRoute = httpRoute;
    }

    //创建它的IHttpRoute 即HostedHttpRoute
    public IHttpRoute HttpRoute { get; private set; }

    //重写了约束检查处理功能,优先使用IHttpRouteConstraint

    //注意请求是HttpContextBase类型的HttpContext,在ASP.NET路由中参数用得是HttpContextBase类型而不是HttpRequestMessage类型

    protected override bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection       routeDirection)
    {
      ValidateConstraint(HttpRoute.RouteTemplate, parameterName, constraint);

      IHttpRouteConstraint httpRouteConstraint = constraint as IHttpRouteConstraint;
      if (httpRouteConstraint != null)
      {

        //如果是IHttpRouteConstraint

        //创建一个HttpRequestMessage 
        HttpRequestMessage request = httpContext.GetOrCreateHttpRequestMessage();
        return httpRouteConstraint.Match(request, HttpRoute, parameterName, values, ConvertRouteDirection(routeDirection));
      }

      //不是的话,调用基类ASP.NET路由对应检查功能

      return base.ProcessConstraint(httpContext, constraint, parameterName, values, routeDirection);
    }

    //重写了路由解析功能,注意参数也是HttpContextBase

    //注意返回值都是ASP.NET路由中的数据对象RouteData

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
      try
      {
        if (HttpRoute is HostedHttpRoute)
        {

          //Web Host模式下,就调用基类ASP.NET路由对应的功能
          return base.GetRouteData(httpContext);
        }
        else
        {

          //其他情况就调用,创建本对象的HttpRoute自身的功能,也要构建HttpRequestMessage 
          HttpRequestMessage request = httpContext.GetOrCreateHttpRequestMessage();
          IHttpRouteData data = HttpRoute.GetRouteData(httpContext.Request.ApplicationPath, request);
          return data == null ? null : data.ToRouteData();
        }
      }
      catch (Exception exception)
      {
        ExceptionDispatchInfo exceptionInfo = ExceptionDispatchInfo.Capture(exception);
        return new RouteData(this, new HttpRouteExceptionRouteHandler(exceptionInfo));
      }
    }

    //重写了生成URL功能,主要逻辑和GetRouteData情况一样

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
      if (!values.ContainsKey(HttpRouteKey))
      {
        return null;
      }

      RouteValueDictionary newValues = GetRouteDictionaryWithoutHttpRouteKey(values);

      if (HttpRoute is HostedHttpRoute)
      {
        return base.GetVirtualPath(requestContext, newValues);
      }
      else
      {
        HttpRequestMessage request = requestContext.HttpContext.GetOrCreateHttpRequestMessage();
        IHttpVirtualPathData virtualPathData = HttpRoute.GetVirtualPath(request, values);

        return virtualPathData == null ? null : new VirtualPathData(this, virtualPathData.VirtualPath);
      }
    }

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

    private static HttpRouteDirection ConvertRouteDirection(RouteDirection routeDirection)
    {
      if (routeDirection == RouteDirection.IncomingRequest)
      {
        return HttpRouteDirection.UriResolution;
      }

      if (routeDirection == RouteDirection.UrlGeneration)
      {
        return HttpRouteDirection.UriGeneration;
      }

      throw Error.InvalidEnumArgument("routeDirection", (int)routeDirection, typeof(RouteDirection));
    }

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

      if (constraint is IRouteConstraint)
      {
        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,
        typeof(IRouteConstraint).FullName);
    }
  }

2、HostedHttpRoute

   HostedHttpRoute是全局路由表(HostedHttpRouteCollection)创建路由(调用CreateRoute方法)时候创建的对象,并将其添加到路由表中,在其构造函数中创建了一个HttpWebRoute(真正实施路由的对象,即1、HttpWebRoute)对象的OriginalRoute 属性,从前边得知HttpWebRoute继承Route,相当于是对ASP.NET 路由功能的间接封装。

  另外,该类实现了HttpRoute接口,其中的GetRouteData、GetVirtualPath参数的去求上下文信息是之前保存在HttpRequestMessage的属性字典中,KEY值为MS_HttpContext,因为ASP.NET 路由中对应方法是用HttpContext的,而不是HttpRequestMessage,所以需要从其字典属性中去取对应请求上下文数据。

  总结下来它是对ASP.NET路由系统中Route对象间接封装。

  internal class HostedHttpRoute : IHttpRoute
  {
    public HostedHttpRoute(string uriTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens,       HttpMessageHandler handler)
    {
      RouteValueDictionary routeDefaults = defaults != null ? new RouteValueDictionary(defaults) : null;
      RouteValueDictionary routeConstraints = constraints != null ? new RouteValueDictionary(constraints) : null;
      RouteValueDictionary routeDataTokens = dataTokens != null ? new RouteValueDictionary(dataTokens) : null;

      //直接创建HttpWebRoute,保存到OriginalRoute 属性,同时指定了HttpControllerRouteHandler作为IRouteHandler
      OriginalRoute = new HttpWebRoute(uriTemplate, routeDefaults, routeConstraints, routeDataTokens, HttpControllerRouteHandler.Instance, this);
      Handler = handler;
    }

    public string RouteTemplate
    {
      get { return OriginalRoute.Url; }
    }

    public IDictionary<string, object> Defaults
    {
      get { return OriginalRoute.Defaults; }
    }

    public IDictionary<string, object> Constraints
    {
      get { return OriginalRoute.Constraints; }
    }

    public IDictionary<string, object> DataTokens
    {
      get { return OriginalRoute.DataTokens; }
    }

    public HttpMessageHandler Handler { get; private set; }

    internal Route OriginalRoute { get; private set; }

    //实现了接口的GetRouteData

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

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

      //从HttpRequestMessage字典属性中获取Key为MS_HttpContext的上下文数据

      HttpContextBase httpContextBase = request.GetHttpContext();
      if (httpContextBase == null)
      {
        httpContextBase = new HttpRequestMessageContextWrapper(rootVirtualPath, request);
      }

      //通过OriginalRoute属性的同名方法调用ASP.NET路由的同名方法,并把上下文数据作为参数

      RouteData routeData = OriginalRoute.GetRouteData(httpContextBase);
      if (routeData != null)
      {

        //对返回的ASP.NET 路由数据RouteData封装成HostedHttpRouteData
        return new HostedHttpRouteData(routeData);
      }

      return null;
    }

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

      //从HttpRequestMessage字典属性中获取Key为MS_HttpContext的上下文数据

      HttpContextBase httpContextBase = request.GetHttpContext();
      if (httpContextBase != null)
      {

        //还要从HttpRequestMessage中获取对应解析好的路由数据
        HostedHttpRouteData routeData = request.GetRouteData() as HostedHttpRouteData;
        if (routeData != null)
        {

          //构建出一个RequestContext 对象,其封装了httpContextBase和routeData .OriginalRouteData(即RouteData)
          RequestContext requestContext = new RequestContext(httpContextBase, routeData.OriginalRouteData);

          //通过OriginalRoute调用ASP.NET路由系统的同名方法,并以RequestContext 作为参数
          VirtualPathData virtualPathData = OriginalRoute.GetVirtualPath(requestContext, new RouteValueDictionary(values));
          if (virtualPathData != null)
          {

            //封装成HostedHttpVirtualPathData,routeData.Route为HttpWebRoute
            return new HostedHttpVirtualPathData(virtualPathData, routeData.Route);
          }
        }
      }

      return null;
    }
  }

3、HostedHttpRouteData 

  是对ASP.NET路由中RouteData 的封装,其属性OriginalRouteData保存了被封装的RouteData,被封装的RouteData(即OriginalRouteData)的Route属性是HttpWebRoute。

  internal class HostedHttpRouteData : IHttpRouteData
  {
    public HostedHttpRouteData(RouteData routeData)
    {
      if (routeData == null)
      {
        throw Error.ArgumentNull("routeData");
      }

      OriginalRouteData = routeData;

      HttpWebRoute route = routeData.Route as HttpWebRoute;
      Route = route == null ? null : route.HttpRoute;
    }

    public IHttpRoute Route { get; private set; }

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

    internal RouteData OriginalRouteData { get; private set; }
  }

4、HostedHttpVirtualPathData 

  是对ASP.NET路由中VirtualPathData 的封装,返回的VirtualPath就是对应被封装的VirtualPathData 的VirtualPath属性

  internal class HostedHttpVirtualPathData : IHttpVirtualPathData
  {
    private readonly VirtualPathData _virtualPath;

    public HostedHttpVirtualPathData(VirtualPathData virtualPath, IHttpRoute httpRoute)
    {
      if (virtualPath == null)
      {
        throw Error.ArgumentNull("route");
      }

      _virtualPath = virtualPath;
      Route = httpRoute;
    }

    public IHttpRoute Route { get; private set; }

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

5、HostedHttpRouteCollection 

  是Web Host模式下的全局路由表,由GlobalConfiguration类构建HttpConfiguration对象时候,在构造函数中默认指定,在创建对象HostedHttpRouteCollection 本身时候,在构造函数中,又指定了一个ASP.NET路由的路由表RouteTable.Routes,其用来真正保存最后的路由对象Route,路由注册时候创建的对象,最终都要转换成Route,并存放到ASP.NET路由的路由表RouteTable.Routes中,其类型为RouteCollection ,GlobalConfiguration中有下边这么一句,都是默认写死的

  HttpConfiguration config = new HttpConfiguration(new HostedHttpRouteCollection(RouteTable.Routes));

  简要说明中已经说过,后边还会说明。

  另外,其继承自HttpRouteCollection,重写其主要方法,GetRouteData和GetVirtualPath方法内部调用的是内部路由表RouteTable.Routes类型为RouteCollection的对应同名方法。

  注册路由时候,HttpRouteCollection定义了扩展方法MapHttpRoute,其内部分别调用了CreateRoute和Add方法,来完成注册,如下代码片段

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

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

  由于扩展方法有继承性,扩展一个类型的时候,其也扩展了派生类,所以实际是调用HostedHttpRouteCollection对应CreateRoute和Add方法。

  总结下来它是对ASP.NET全局路由表的封装,通过组合方式

  internal class HostedHttpRouteCollection : HttpRouteCollection
  {
    private readonly RouteCollection _routeCollection;
    private readonly string _virtualPathRoot;

    public HostedHttpRouteCollection(RouteCollection routeCollection)
      : this(routeCollection, virtualPathRoot: null)
    {
    }

    public HostedHttpRouteCollection(RouteCollection routeCollection, string virtualPathRoot)
    {
      if (routeCollection == null)
      {
        throw Error.ArgumentNull("routeCollection");
      }

      _routeCollection = routeCollection;
      _virtualPathRoot = virtualPathRoot;
    }

    public override string VirtualPathRoot
    {
      get
      {
        if (_virtualPathRoot == null)
        {
          return HostingEnvironment.ApplicationVirtualPath;
        }
        else
        {
          return _virtualPathRoot;
        }
      }
    }

    public override int Count
    {
      get { return _routeCollection.Count; }
    }

    public override IHttpRoute this[string name]
    {
      get
      {
        HttpWebRoute route = _routeCollection[name] as HttpWebRoute;
        if (route != null)
        {
          return route.HttpRoute;
        }

        throw Error.KeyNotFound();
      }
    }

    public override IHttpRoute this[int index]
    {
      get
      {
        HttpWebRoute route = _routeCollection[index] as HttpWebRoute;
        if (route != null)
        {
          return route.HttpRoute;
        }

        throw Error.ArgumentOutOfRange("index", index, SRResources.RouteCollectionOutOfRange);
      }
    }

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

      HttpContextBase httpContextBase = request.GetHttpContext();
      if (httpContextBase == null)
      {
        httpContextBase = new HttpRequestMessageContextWrapper(VirtualPathRoot, request);
      }

      if (httpContextBase.GetHttpRequestMessage() == null)
      {
        httpContextBase.SetHttpRequestMessage(request);
      }

      //调用内部ASP.NET路由的全局路由表对应方法

      RouteData routeData = _routeCollection.GetRouteData(httpContextBase);

      if (routeData != null && !(routeData.RouteHandler is System.Web.Routing.StopRoutingHandler))
      {

        //返回HostedHttpRouteData,返回结果的声明是IHttpRouteData
        return new HostedHttpRouteData(routeData);
      }

      return null;
    }

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

      HttpContextBase httpContextBase = request.GetHttpContext();
      if (httpContextBase == null)
      {
        httpContextBase = new HttpRequestMessageContextWrapper(VirtualPathRoot, request);
      }

      if (httpContextBase.GetHttpRequestMessage() == null)
      {
        httpContextBase.SetHttpRequestMessage(request);
      }

      IHttpRouteData routeData = request.GetRouteData();
      if (routeData == null)
      {
        return null;
      }

      RequestContext requestContext = new RequestContext(httpContextBase, routeData.ToRouteData());
      RouteValueDictionary routeValues = values != null ? new RouteValueDictionary(values) : new RouteValueDictionary();

      //调用内部ASP.NET路由的全局路由表对应方法
      VirtualPathData virtualPathData = _routeCollection.GetVirtualPath(requestContext, name, routeValues);

      if (virtualPathData != null)
      {
        if (!(virtualPathData.Route is HttpWebRoute))
        {
          if (routeValues.Remove(HttpWebRoute.HttpRouteKey))
          {
            VirtualPathData virtualPathDataWithoutHttpRouteValue = _routeCollection.GetVirtualPath(requestContext, name, routeValues);
            if (virtualPathDataWithoutHttpRouteValue != null)
            {
              virtualPathData = virtualPathDataWithoutHttpRouteValue;
            }
          }
        }

        //返回HostedHttpVirtualPathData,返回结果的声明是IHttpVirtualPathData

        return new HostedHttpVirtualPathData(virtualPathData, routeData.Route);
      }

      return null;
    }

    //路由映射时候创建路由
    public override IHttpRoute CreateRoute(string uriTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object>       dataTokens, HttpMessageHandler handler)
    {
      if (constraints != null)
      {
        foreach (var constraint in constraints)
        {
          ValidateConstraint(uriTemplate, constraint.Key, constraint.Value);
        }
      }

      //每次创建的是这个路由对象,内部创建了一个继承自Route的HttpWebRoute对象

      return new HostedHttpRoute(uriTemplate, defaults, constraints, dataTokens, handler);
    }

    protected override void ValidateConstraint(string routeTemplate, string name, object constraint)
    {
      HttpWebRoute.ValidateConstraint(routeTemplate, name, constraint);
    }

    //添加一个路由对象

    public override void Add(string name, IHttpRoute route)
    {

      //往内部ASP.NET路由系统全局路由表添加Route对象,通过ToRoute方法转换
      _routeCollection.Add(name, route.ToRoute());
    }

    public override void Clear()
    {
      _routeCollection.Clear();
    }

    public override bool Contains(IHttpRoute item)
    {
      foreach (RouteBase route in _routeCollection)
      {
        HttpWebRoute webRoute = route as HttpWebRoute;
        if (webRoute != null && webRoute.HttpRoute == item)
        {
          return true;
        }
      }

      return false;
    }

    public override bool ContainsKey(string name)
    {
      return _routeCollection[name] != null;
    }

    public override IEnumerator<IHttpRoute> GetEnumerator()
    {
      return _routeCollection
      .OfType<HttpWebRoute>()
      .Select(httpWebRoute => httpWebRoute.HttpRoute)
      .GetEnumerator();
    }

    public override bool TryGetValue(string name, out IHttpRoute route)
    {
      HttpWebRoute rt = _routeCollection[name] as HttpWebRoute;
      if (rt != null)
      {
        route = rt.HttpRoute;
        return true;
      }

      route = null;
      return false;
    }

    private static NotSupportedException NotSupportedByRouteCollection()
    {
      return Error.NotSupported(SRResources.RouteCollectionNotSupported, typeof(HostedHttpRouteCollection).Name);
    }

    private static NotSupportedException NotSupportedByHostedRouteCollection()
    {
      return Error.NotSupported(SRResources.RouteCollectionUseDirectly, typeof(RouteCollection).Name);
    }
  }

6、GlobalConfiguration

  public static class GlobalConfiguration
  {
    private static Lazy<HttpConfiguration> _configuration = CreateConfiguration();

    private static Lazy<HttpMessageHandler> _defaultHandler = CreateDefaultHandler();

    private static Lazy<HttpServer> _defaultServer = CreateDefaultServer();

    public static HttpConfiguration Configuration
    {
      get { return _configuration.Value; }
    }

    public static HttpMessageHandler DefaultHandler
    {
      get { return _defaultHandler.Value; }
    }

    public static HttpServer DefaultServer
    {
      get { return _defaultServer.Value; }
    }

    public static void Configure(Action<HttpConfiguration> configurationCallback)
    {
      if (configurationCallback == null)
      {
        throw new ArgumentNullException("configurationCallback");
      }

      configurationCallback.Invoke(Configuration);
      Configuration.EnsureInitialized();
    }

    internal static void Reset()
    {
      _configuration = CreateConfiguration();
      _defaultHandler = CreateDefaultHandler();
      _defaultServer = CreateDefaultServer();
    }

    private static Lazy<HttpConfiguration> CreateConfiguration()
    {
      return new Lazy<HttpConfiguration>(() =>
      {

       //默认指定,HostedHttpRouteCollection,并传入ASP.NET路由系统的全局路由表
        HttpConfiguration config = new HttpConfiguration(new HostedHttpRouteCollection(RouteTable.Routes));
        ServicesContainer services = config.Services;
        Contract.Assert(services != null);
        services.Replace(typeof(IAssembliesResolver), new WebHostAssembliesResolver());
        services.Replace(typeof(IHttpControllerTypeResolver), new WebHostHttpControllerTypeResolver());
        services.Replace(typeof(IHostBufferPolicySelector), new WebHostBufferPolicySelector());
        services.Replace(typeof(IExceptionHandler),
        new WebHostExceptionHandler(services.GetExceptionHandler()));
        return config;
      });
    }

    private static Lazy<HttpMessageHandler> CreateDefaultHandler()
    {
      return new Lazy<HttpMessageHandler>(() => new HttpRoutingDispatcher(_configuration.Value));
    }

    //默认指定第一个HttpMessageHandler

    private static Lazy<HttpServer> CreateDefaultServer()
    {
      return new Lazy<HttpServer>(() => new HttpServer(_configuration.Value, _defaultHandler.Value));
    }

  }

7、HttpControllerRouteHandler 

  在HostedHttpRoute构造函数中创建HttpWebRoute对象时候,已经直接指定每个请求路由匹配后使用的IRouteHandler,其返回的IHttpHandler 用来处理请求。

  OriginalRoute = new HttpWebRoute(uriTemplate, routeDefaults, routeConstraints, routeDataTokens, HttpControllerRouteHandler.Instance, this);

  public class HttpControllerRouteHandler : IRouteHandler
  {
    private static readonly Lazy<HttpControllerRouteHandler> _instance =
      new Lazy<HttpControllerRouteHandler>(() => new HttpControllerRouteHandler(), isThreadSafe: true);

    protected HttpControllerRouteHandler()
    {
    }

    public static HttpControllerRouteHandler Instance
    {
      get { return _instance.Value; }
    }

    IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
    {
      return GetHttpHandler(requestContext);
    }

    protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
    {

     //直接new了HttpControllerHandler
      return new HttpControllerHandler(requestContext.RouteData);
    }
  }

8、HttpControllerHandler 

  public class HttpControllerHandler : HttpTaskAsyncHandler
  {
    internal static readonly string OwinEnvironmentHttpContextKey = "owin.Environment";

    internal static readonly string OwinEnvironmentKey = "MS_OwinEnvironment";

    private static readonly Lazy<Action<HttpContextBase>> _suppressRedirectAction =
      new Lazy<Action<HttpContextBase>>(
        () =>
        {
          if (!SuppressFormsAuthRedirectHelper.GetEnabled(WebConfigurationManager.AppSettings))
          {
            return httpContext => { };
          }

          return httpContext => httpContext.Response.SuppressFormsAuthenticationRedirect = true;
        });

    private static readonly Lazy<IHostBufferPolicySelector> _bufferPolicySelector =
      new Lazy<IHostBufferPolicySelector>(() => GlobalConfiguration.Configuration.Services.GetHostBufferPolicySelector());

    private static readonly Lazy<IExceptionHandler> _exceptionHandler = new Lazy<IExceptionHandler>(() =>
      ExceptionServices.GetHandler(GlobalConfiguration.Configuration));
    private static readonly Lazy<IExceptionLogger> _exceptionLogger = new Lazy<IExceptionLogger>(() =>
      ExceptionServices.GetLogger(GlobalConfiguration.Configuration));

    private static readonly Func<HttpRequestMessage, X509Certificate2> _retrieveClientCertificate = new Func<HttpRequestMessage, X509Certificate2>                           (RetrieveClientCertificate);

    private readonly IHttpRouteData _routeData;
    private readonly HttpMessageInvoker _server;

    //构造函数中,默认制定了第一个HttpMessageHandler为HttpServer
    public HttpControllerHandler(RouteData routeData)
    : this(routeData, GlobalConfiguration.DefaultServer)
    {
    }

    public HttpControllerHandler(RouteData routeData, HttpMessageHandler handler)
    {
      if (routeData == null)
      {
        throw Error.ArgumentNull("routeData");
      }
      if (handler == null)
      {
        throw Error.ArgumentNull("handler");
      }

      _routeData = new HostedHttpRouteData(routeData);
      _server = new HttpMessageInvoker(handler);
    }

    public override Task ProcessRequestAsync(HttpContext context)
    {
      return ProcessRequestAsyncCore(new HttpContextWrapper(context));
    }

    internal async Task ProcessRequestAsyncCore(HttpContextBase contextBase)
    {

      //创建HttpRequestMessage对象

      HttpRequestMessage request = contextBase.GetHttpRequestMessage() ?? ConvertRequest(contextBase);

      //把路由数据存放到HttpRequestMessage对象的属性字典中

      request.SetRouteData(_routeData);
      CancellationToken cancellationToken = contextBase.Response.GetClientDisconnectedTokenWhenFixed();
      HttpResponseMessage response = null;

      try
      {

        //调用方法将请求转出得到消息管道的第一个处理器,HttpServer(HttpMessageHandler)
        response = await _server.SendAsync(request, cancellationToken);
        await CopyResponseAsync(contextBase, request, response, _exceptionLogger.Value, _exceptionHandler.Value,
          cancellationToken);
      }
      catch (OperationCanceledException)
      {
        contextBase.Request.Abort();
      }
      finally
      {
        request.DisposeRequestResources();
        request.Dispose();

        if (response != null)
        {
          response.Dispose();
        }
      }
    }

  }

二、路由注册

  我们新建一个Web应用程序的Web API项目时候,默认生成路由配置如下图:

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

  那怎么会寄宿到Web Host呢,而且还要用到前边介绍的这些类,来完成与ASP.NET 路由系统的集成,我们看到项目自动引用了程序集System.Web.Http.WebHost程序集,我们的Web Host模式的类都定义到这里,另外,根据前边介绍GlobalConfiguration默认使用了其中的类

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

  但是下边的代码明明是调用的是上一篇介绍的接口方法,config.Routes的类型是HttpRouteCollection,MapHttpRoute是其扩展方法,

  config.Routes.MapHttpRoute

  从前边知道,GlobalConfiguration指定的HostedHttpRouteCollection继承自HttpRouteCollection,对还是下边这句

  HttpConfiguration config = new HttpConfiguration(new HostedHttpRouteCollection(RouteTable.Routes));

  所以config.Routes实际类型是HostedHttpRouteCollection,再利用扩展方法的继承性,MapHttpRoute方法中调用的方法(其中调用了HttpRouteCollection的CreateRoute和Add方法),实际是HostedHttpRouteCollection中的方法。

  所以,我们实现了在Web Host模式下的路由注册。

三、路由解析

  HostedHttpRouteCollection中的GetRouteData方法,其实际调用的是内部封装的ASP.NET路由的全局路由表(RouteCollection类型)的同名方法,请参考前边注释,或参考ASP.NET路由部分。

四、生成URL

  HostedHttpRouteCollection中的GetVirtualPath方法,其实际调用的是内部封装的ASP.NET路由的全局路由表(RouteCollection类型)的同名方法,请参考前边注释,或参考ASP.NET路由部分。

五、如何将请求转出到ASP.NET Web API 的核心消息处理管道

  从前边介绍可知道,我们默认指定了一个IRouteHandler为HttpControllerRouteHandler,其返回一个HttpControllerHandler类型的IHttpHandler,其封装了消息处理管道的第一个消息处理器HttpServer,在其ProcessRequestAsync方法中,会触发其执行,来启动ASP.NET Web API 消息处理管道。

ASP.NET Web API 框架研究 Web Host模式路由及将请求转出到消息处理管道的更多相关文章

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

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

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

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

  3. ASP&period;NET Web API 框架研究 ASP&period;NET Web API 路由

    ASP.NET Web API 核心框架是一个独立的.抽象的消息处理管道,ASP.NET Web API有自己独立的路由系统,是消息处理管道的组成部分,其与ASP.NET路由系统有类似的设计,都能找到 ...

  4. ASP&period;NET Web API 框架研究 ASP&period;NET 路由

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

  5. ASP&period;NET Web API 框架研究 Controller实例的销毁

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

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

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

  7. ASP&period;NET Web API 框架研究 Action方法介绍

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

  8. ASP&period;NET Web API 框架研究 服务容器 ServicesContainer

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

  9. ASP&period;NET Web API 框架研究 IoC容器 DependencyResolver

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

随机推荐

  1. infragistics-webdatagrid

    infragistics-webdatagrid DataRow DataTable DataSet DataColumn stringBuilder append appendFormat 存储过程 ...

  2. WAMPSERVER 与 IIS冲突 及如何修改网站根目录

    如果之前打开过IIS服务或者用过xampp则输入localhost时,浏览器显示如下: 这说明WAMPSERVER 与 IIS服务的默认端口冲突: 可以测试80端口是否被占用: 如上端口被IIS服务占 ...

  3. 消灭textarea中的神秘空格

    之前在做页面的时候经常发现写的textarea中会有一些默认的空格出现,鼠标可以在里面任意点击.这个问题折腾了好久,后来发现,原来是<textarea></textarea>标 ...

  4. C&num;基础知识回顾-- 反射(1)

    C#基础知识回顾-- 反射(1)   反射(reflection)是一种允许用户获得类型信息的C#特性.术语“反射”源自于它的工作方式: Type对象映射它所代表的底层对象.对Type对象进行查询可以 ...

  5. BZOJ 3181&lpar;&lbrack;Coci2012&rsqb;BROJ-最小质因子为p的第k小素数&rpar;

    3181: [Coci2012]BROJ Time Limit: 10 Sec   Memory Limit: 64 MB Submit: 26   Solved: 7 [ Submit][ Stat ...

  6. 自定义不等高的cell-&lpar;storyboard&rpar;

    对比自定义等高cell,需要几个额外的步骤(iOS8开始才支持) 添加子控件和contentView之间的间距约束 设置tableViewCell的真实行高和估算行高 // 告诉tableView所有 ...

  7. Web纯前端&OpenCurlyDoubleQuote;旭日图”实现元素周期表

    一.什么是旭日图 旭日图是在Excel 2016中新增的一种图表.有些类似饼图,饼图的优势是可以显示占比.但是饼图只能显示单级数据.旭日图用来表示多层级数据的占比.旭日图以一种分层方式显示,非常适合用 ...

  8. 微信小程序实现&OpenCurlyDoubleQuote;鲜肉APP”首页效果

    项目地址http://git.oschina.net/djcx/WeiXinXiaoChengXu/tree/master 如果您觉得不错,记得给一个star 由于微信小程序目前是当下趋势,正好昨天弄 ...

  9. SynchronousQueueDemo

    1.ArrayDeque, (数组双端队列) 2.PriorityQueue, (优先级队列) 3.ConcurrentLinkedQueue, (基于链表的并发队列) 4.DelayQueue, ( ...

  10. &period;NET 并行计算和并发10-lock锁

    class Program { private static List<int> intlist; static void Main(string[] args) { intlist = ...