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类,主要类和成员如下图:
简要说明:
在静态类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项目时候,默认生成路由配置如下图:
那怎么会寄宿到Web Host呢,而且还要用到前边介绍的这些类,来完成与ASP.NET 路由系统的集成,我们看到项目自动引用了程序集System.Web.Http.WebHost程序集,我们的Web Host模式的类都定义到这里,另外,根据前边介绍GlobalConfiguration默认使用了其中的类
但是下边的代码明明是调用的是上一篇介绍的接口方法,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模式路由及将请求转出到消息处理管道的更多相关文章
-
ASP.NET Web API 框架研究 Self Host模式下的消息处理管道
Self Host模式下的ASP.NET Web API与WCF非常相似,都可以寄宿在任意类型的托管应用程序中,宿主可以是Windows Form .WPF.控制台应用以及Windows Servic ...
-
ASP.NET Web API 框架研究 Web Host模式下的消息处理管道
寄宿的作用是开启一个进程为Web API提供一个运行环境以解决持续监听.请求监听和响应回复,即将接收到的请求转换成HttpRequestMessage对象传入管道,并将管道生成并经过处理后的HttpR ...
-
ASP.NET Web API 框架研究 ASP.NET Web API 路由
ASP.NET Web API 核心框架是一个独立的.抽象的消息处理管道,ASP.NET Web API有自己独立的路由系统,是消息处理管道的组成部分,其与ASP.NET路由系统有类似的设计,都能找到 ...
-
ASP.NET Web API 框架研究 ASP.NET 路由
ASP.NET Web API 如果采用Web Host方式来寄宿,在请求进入Web API 消息处理管道之前,就会用ASP.NET 自身的路由系统根据注册的路由表,解析出当前请求的HttpContr ...
-
ASP.NET Web API 框架研究 Controller实例的销毁
我们知道项目中创建的Controller,如ProductController都继承自ApiController抽象类,其又实现了接口IDisposable,所以,框架中自动调用Dispose方法来释 ...
-
ASP.NET Web API 框架研究 核心的消息处理管道
ASP.NET Web API 的核心框架是一个由一组HttpMessageHandler有序组成的双工消息处理管道:寄宿监听到请求接受后,把消息传入该管道经过所有HttpMessageHandler ...
-
ASP.NET Web API 框架研究 Action方法介绍
在根据请求解析出匹配的Controller类型并创建实例后,要在该Controller类型中的众多Action方法中选择与请求匹配的那一个,并执行,然后返回响应. Action方法,其元数据,主要包括 ...
-
ASP.NET Web API 框架研究 服务容器 ServicesContainer
ServicesContainer是一个服务的容器,可以理解为—个轻量级的IoC容器,其维护着一个服务接口类型与服务实例之间的映射关系,可以根据服务接口类型获取对应的服务实例.构成ASP.NET We ...
-
ASP.NET Web API 框架研究 IoC容器 DependencyResolver
一.概念 1.IoC(Inversion of Control),控制反转 即将依赖对象的创建和维护交给一个外部容器来负责,而不是应用本身.如,在类型A中需要使用类型B的实例,而B的实例的创建不是由A ...
随机推荐
-
infragistics-webdatagrid
infragistics-webdatagrid DataRow DataTable DataSet DataColumn stringBuilder append appendFormat 存储过程 ...
-
WAMPSERVER 与 IIS冲突 及如何修改网站根目录
如果之前打开过IIS服务或者用过xampp则输入localhost时,浏览器显示如下: 这说明WAMPSERVER 与 IIS服务的默认端口冲突: 可以测试80端口是否被占用: 如上端口被IIS服务占 ...
-
消灭textarea中的神秘空格
之前在做页面的时候经常发现写的textarea中会有一些默认的空格出现,鼠标可以在里面任意点击.这个问题折腾了好久,后来发现,原来是<textarea></textarea>标 ...
-
C#基础知识回顾-- 反射(1)
C#基础知识回顾-- 反射(1) 反射(reflection)是一种允许用户获得类型信息的C#特性.术语“反射”源自于它的工作方式: Type对象映射它所代表的底层对象.对Type对象进行查询可以 ...
-
BZOJ 3181([Coci2012]BROJ-最小质因子为p的第k小素数)
3181: [Coci2012]BROJ Time Limit: 10 Sec Memory Limit: 64 MB Submit: 26 Solved: 7 [ Submit][ Stat ...
-
自定义不等高的cell-(storyboard)
对比自定义等高cell,需要几个额外的步骤(iOS8开始才支持) 添加子控件和contentView之间的间距约束 设置tableViewCell的真实行高和估算行高 // 告诉tableView所有 ...
-
Web纯前端“旭日图”实现元素周期表
一.什么是旭日图 旭日图是在Excel 2016中新增的一种图表.有些类似饼图,饼图的优势是可以显示占比.但是饼图只能显示单级数据.旭日图用来表示多层级数据的占比.旭日图以一种分层方式显示,非常适合用 ...
-
微信小程序实现“鲜肉APP”首页效果
项目地址http://git.oschina.net/djcx/WeiXinXiaoChengXu/tree/master 如果您觉得不错,记得给一个star 由于微信小程序目前是当下趋势,正好昨天弄 ...
-
SynchronousQueueDemo
1.ArrayDeque, (数组双端队列) 2.PriorityQueue, (优先级队列) 3.ConcurrentLinkedQueue, (基于链表的并发队列) 4.DelayQueue, ( ...
-
.NET 并行计算和并发10-lock锁
class Program { private static List<int> intlist; static void Main(string[] args) { intlist = ...