尊重作者劳动成果,转载请注明出处,谢谢!
目录
1.请求管道
1.1 请求上下文
1.2 服务器
1.3 中间件
2.中间件
2.1 注册中间件
2.2 构建请求管道
2.3 IApplicationBuilder 扩展方法
3.实现解析
3.1 设计模型
3.2 ApplicationBuilder
3.3 HostingApplication
3.4 DefaultHttpContextFactory
1.请求管道
在 Web 主机一节中,我们简单介绍到了 ASP.NET Core 对 HTTP 请求的处理是由一个请求处理管道来完成的,位于管道最前面的是一个 Web 服务器,用于监听、接收并响应 HTTP 请求,Web 服务器后面是一组中间件,用于对接收到的请求进行处理。服务器接收到 HTTP 请求后,首先会根据其特性创建一个请求上下文,该上下文贯穿了整个处理过程,然后将上下文交给后续的中间件进行处理,每个中间处理完之后会将该上下文交由下一个中间件进行处理,直到最后一个中间件处理完成后再按相反的顺序进行返回。整个请求的处理过程就像是一个管道一样,如下图所示:
1.1 请求上下文
HttpContext 表示请求上下文,是一个抽象类,其中包含了请求信息、响应信息、依赖注入容器对象等,定义如下:
public abstract class HttpContext { public abstract IFeatureCollection Features { get; } public abstract HttpRequest Request { get; } public abstract HttpResponse Response { get; } public abstract ConnectionInfo Connection { get; } public abstract WebSocketManager WebSockets { get; } public abstract ClaimsPrincipal User { get; set; } public abstract IDictionary<object, object> Items { get; set; } public abstract IServiceProvider RequestServices { get; set; } public abstract CancellationToken RequestAborted { get; set; } public abstract string TraceIdentifier { get; set; } public abstract ISession Session { get; set; } public abstract void Abort(); }
HttpRequest 表示请求的相关信息,是一个抽象类,定义如下:
public abstract class HttpRequest { public abstract HttpContext HttpContext { get; } public abstract string Method { get; set; } public abstract string Scheme { get; set; } public abstract bool IsHttps { get; set; } public abstract HostString Host { get; set; } public abstract PathString PathBase { get; set; } public abstract PathString Path { get; set; } public abstract QueryString QueryString { get; set; } public abstract IQueryCollection Query { get; set; } public abstract string Protocol { get; set; } public abstract IHeaderDictionary Headers { get; } public abstract IRequestCookieCollection Cookies { get; set; } public abstract long? ContentLength { get; set; } public abstract string ContentType { get; set; } public abstract Stream Body { get; set; } public virtual PipeReader BodyReader { get => throw new NotImplementedException(); } public abstract bool HasFormContentType { get; } public abstract IFormCollection Form { get; set; } public abstract Task<IFormCollection> ReadFormAsync(CancellationToken cancellationToken = new CancellationToken()); public virtual RouteValueDictionary RouteValues { get; set; } }
HttpResponse 表示请求的响应信息,是一个抽象类,定义如下:
public abstract class HttpResponse { private static readonly Func<object, Task> _callbackDelegate = callback => ((Func<Task>)callback)(); private static readonly Func<object, Task> _disposeDelegate = disposable => { ((IDisposable)disposable).Dispose(); return Task.CompletedTask; }; private static readonly Func<object, Task> _disposeAsyncDelegate = disposable => ((IAsyncDisposable)disposable).DisposeAsync().AsTask(); public abstract HttpContext HttpContext { get; } public abstract int StatusCode { get; set; } public abstract IHeaderDictionary Headers { get; } public abstract Stream Body { get; set; } public virtual PipeWriter BodyWriter { get => throw new NotImplementedException(); } public abstract long? ContentLength { get; set; } public abstract string ContentType { get; set; } public abstract IResponseCookies Cookies { get; } public abstract bool HasStarted { get; } public abstract void OnStarting(Func<object, Task> callback, object state); public virtual void OnStarting(Func<Task> callback) => OnStarting(_callbackDelegate, callback); public abstract void OnCompleted(Func<object, Task> callback, object state); public virtual void RegisterForDispose(IDisposable disposable) => OnCompleted(_disposeDelegate, disposable); public virtual void RegisterForDisposeAsync(IAsyncDisposable disposable) => OnCompleted(_disposeAsyncDelegate, disposable); public virtual void OnCompleted(Func<Task> callback) => OnCompleted(_callbackDelegate, callback); public virtual void Redirect(string location) => Redirect(location, permanent: false); public abstract void Redirect(string location, bool permanent); public virtual Task StartAsync(CancellationToken cancellationToken = default) { throw new NotImplementedException(); } public virtual Task CompleteAsync() { throw new NotImplementedException(); } }
1.2 服务器
IServer 表示服务器,定义如下:
public interface IServer : IDisposable { IFeatureCollection Features { get; } Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken); Task StopAsync(CancellationToken cancellationToken); }
StartAsync 方法用于启动 Web 服务器,并开始处理 HTTP 请求,该方法需要传入一个 IHttpApplication<TContext> 类型的参数,IHttpApplication<TContext> 接口表示 Web 服务器对 HTTP 请求的处理流程,即对 IServer 接口接收到的原始请求的处理,包括创建请求上下文,将请求上下文交由中间件处理,最后释放该上下文,都是通过 IHttpApplication<TContext> 接口来实现的。StopAsync 方法用于停止 Web 服务器。IHttpApplication<TContext> 接口的定义如下:
public interface IHttpApplication<TContext> { TContext CreateContext(IFeatureCollection contextFeatures); Task ProcessRequestAsync(TContext context); void DisposeContext(TContext context, Exception exception); }
CreateContext 方法用于从原始的请求中创建出对应的请求上下文对象,接着由 ProcessRequestAsync 方法对该上下文进行处理,最后由 DisposeContext 方法释放该上下文信息。
IServer 接口有一个 IFeatureCollection 类型的属性,IFeatureCollection 接口用于存取与服务和请求上下文相关的特性信息,其本质是一个字典类型。定义如下:
[DefaultMember("Item")] public interface IFeatureCollection : IEnumerable<KeyValuePair<Type, object>>, IEnumerable { object this[Type key] { get; set; } bool IsReadOnly { get; } int Revision { get; } TFeature Get<TFeature>(); void Set<TFeature>(TFeature instance); }
在创建请求上下文对象时,CreateContext 方法会根据服务器上的特性集合来创建具体的请求上下文对象(继承自 HttpContext)。
1.3 中间件
从上图中我们知道,一个中间件要对请求进行处理,需要接收一个请求上下文的对象,并将请求上下文“传递”给下一个中间件。ASP.NET Core 专门定义了一个 RequestDelegate 委托类型来表示对请求的处理,如下代码所示:
public delegate Task RequestDelegate(HttpContext context);
所以,对于中间件的定义,ASP.NET Core 使用一个 Func<RequestDelegate, RequestDelegate> 的委托类型来进行描述,该委托类型接收一个 RequestDelegate 类型的参数,并返回了新的 RequestDelegate 对象,即下一个中间件的输出作为前一个中间件的输入参数。通过这种“链接”方式,将所有的中间件连接了起来,形成了一个完整的请求管道。通过上面的描述,最终的请求管道也表现为一个 RequestDelegate 对象。
2.中间件
请求管道中最重要的组成部分就是中间件,一个 ASP.NET Core 应用简单点来说就是中间件的组合,要使我们的应用具备处理请求的能力,我们应该根据需要注册相应的中间件。ASP.NET Core 为我们提供了丰富的中间件,如缓存、会话、认证、授权等,我们也可以根据需求定义自己的中间件,并将其注册到管道中来实现对请求的处理。在实际项目开发中,对于中间件的注册,主要是通过 Startup 启动类来完成的。根据约定的命名规则,框架会自动调用 Startup 启动类的 Configure 方法来完成对中间件的注册,该方法默认的第一个参数为 Action<IApplicationBuilder> 委托类型,实际上我们正是通过 IApplicationBuilder 接口来完成对中间件的注册的。IApplicationBuilder 接口的定义如下:
public interface IApplicationBuilder { IServiceProvider ApplicationServices { get; set; } IFeatureCollection ServerFeatures { get; } IDictionary<string, object> Properties { get; } IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware); IApplicationBuilder New(); RequestDelegate Build(); }
2.1 注册中间件
2.1.1 注册委托类型中间件
Use 方法用于注册中间件,其参数是一个 Func<RequestDelegate, RequestDelegate> 类型。
2.1.2 注册强类型和约定名称中间件
UseMiddleware 方法是 IApplicationBuilder 的扩展方法,用于注册强类型或约定名称的中间件,如下代码所示:
public static class UseMiddlewareExtensions { internal const string InvokeMethodName = "Invoke"; internal const string InvokeAsyncMethodName = "InvokeAsync"; private static readonly MethodInfo GetServiceInfo = typeof(UseMiddlewareExtensions).GetMethod(nameof(GetService), BindingFlags.NonPublic | BindingFlags.Static); public static IApplicationBuilder UseMiddleware<TMiddleware>(this IApplicationBuilder app, params object[] args) { return app.UseMiddleware(typeof(TMiddleware), args); } public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, Type middleware, params object[] args) { if (typeof(IMiddleware).GetTypeInfo().IsAssignableFrom(middleware.GetTypeInfo())) { // IMiddleware doesn't support passing args directly since it's // activated from the container if (args.Length > 0) { throw new NotSupportedException(Resources.FormatException_UseMiddlewareExplicitArgumentsNotSupported(typeof(IMiddleware))); } return UseMiddlewareInterface(app, middleware); } var applicationServices = app.ApplicationServices; return app.Use(next => { var methods = middleware.GetMethods(BindingFlags.Instance | BindingFlags.Public); var invokeMethods = methods.Where(m => string.Equals(m.Name, InvokeMethodName, StringComparison.Ordinal) || string.Equals(m.Name, InvokeAsyncMethodName, StringComparison.Ordinal) ).ToArray(); if (invokeMethods.Length > 1) { throw new InvalidOperationException(Resources.FormatException_UseMiddleMutlipleInvokes(InvokeMethodName, InvokeAsyncMethodName)); } if (invokeMethods.Length == 0) { throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoInvokeMethod(InvokeMethodName, InvokeAsyncMethodName, middleware)); } var methodInfo = invokeMethods[0]; if (!typeof(Task).IsAssignableFrom(methodInfo.ReturnType)) { throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNonTaskReturnType(InvokeMethodName, InvokeAsyncMethodName, nameof(Task))); } var parameters = methodInfo.GetParameters(); if (parameters.Length == 0 || parameters[0].ParameterType != typeof(HttpContext)) { throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoParameters(InvokeMethodName, InvokeAsyncMethodName, nameof(HttpContext))); } var ctorArgs = new object[args.Length + 1]; ctorArgs[0] = next; Array.Copy(args, 0, ctorArgs, 1, args.Length); var instance = ActivatorUtilities.CreateInstance(app.ApplicationServices, middleware, ctorArgs); if (parameters.Length == 1) { return (RequestDelegate)methodInfo.CreateDelegate(typeof(RequestDelegate), instance); } var factory = Compile<object>(methodInfo, parameters); return context => { var serviceProvider = context.RequestServices ?? applicationServices; if (serviceProvider == null) { throw new InvalidOperationException(Resources.FormatException_UseMiddlewareIServiceProviderNotAvailable(nameof(IServiceProvider))); } return factory(instance, context, serviceProvider); }; }); } private static IApplicationBuilder UseMiddlewareInterface(IApplicationBuilder app, Type middlewareType) { return app.Use(next => { return async context => { var middlewareFactory = (IMiddlewareFactory)context.RequestServices.GetService(typeof(IMiddlewareFactory)); if (middlewareFactory == null) { // No middleware factory throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoMiddlewareFactory(typeof(IMiddlewareFactory))); } var middleware = middlewareFactory.Create(middlewareType); if (middleware == null) { // The factory returned null, it's a broken implementation throw new InvalidOperationException(Resources.FormatException_UseMiddlewareUnableToCreateMiddleware(middlewareFactory.GetType(), middlewareType)); } try { await middleware.InvokeAsync(context, next); } finally { middlewareFactory.Release(middleware); } }; }); } private static Func<T, HttpContext, IServiceProvider, Task> Compile<T>(MethodInfo methodInfo, ParameterInfo[] parameters) { var middleware = typeof(T); var httpContextArg = Expression.Parameter(typeof(HttpContext), "httpContext"); var providerArg = Expression.Parameter(typeof(IServiceProvider), "serviceProvider"); var instanceArg = Expression.Parameter(middleware, "middleware"); var methodArguments = new Expression[parameters.Length]; methodArguments[0] = httpContextArg; for (int i = 1; i < parameters.Length; i++) { var parameterType = parameters[i].ParameterType; if (parameterType.IsByRef) { throw new NotSupportedException(Resources.FormatException_InvokeDoesNotSupportRefOrOutParams(InvokeMethodName)); } var parameterTypeExpression = new Expression[] { providerArg, Expression.Constant(parameterType, typeof(Type)), Expression.Constant(methodInfo.DeclaringType, typeof(Type)) }; var getServiceCall = Expression.Call(GetServiceInfo, parameterTypeExpression); methodArguments[i] = Expression.Convert(getServiceCall, parameterType); } Expression middlewareInstanceArg = instanceArg; if (methodInfo.DeclaringType != typeof(T)) { middlewareInstanceArg = Expression.Convert(middlewareInstanceArg, methodInfo.DeclaringType); } var body = Expression.Call(middlewareInstanceArg, methodInfo, methodArguments); var lambda = Expression.Lambda<Func<T, HttpContext, IServiceProvider, Task>>(body, instanceArg, httpContextArg, providerArg); return lambda.Compile(); } private static object GetService(IServiceProvider sp, Type type, Type middleware) { var service = sp.GetService(type); if (service == null) { throw new InvalidOperationException(Resources.FormatException_InvokeMiddlewareNoService(type, middleware)); } return service; } }
UseMiddleware 方法首先判断注册的中间件是否实现于 IMiddleware 接口,如果实现了该接口,则代表注册的中间件是强类型的,该接口定义了唯一的 InvokeAsync 方法,用于处理请求上下文对象,并决定是否将请求传递给下一个中间件进行处理。IMiddleware 接口的定义如下:
public interface IMiddleware { Task InvokeAsync(HttpContext context, RequestDelegate next); }
2.1.3 注册约定名称中间件
UseMiddleware
2.2 构建请求管道
Build
2.3 IApplicationBuilder 扩展方法
Run
UseWhen
UsePathBase