zoukankan      html  css  js  c++  java
  • ASP.NET Core 3 源码解析 — [9]请求管道

    尊重作者劳动成果,转载请注明出处,谢谢!

    目录

    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

    3.实现解析

    3.1 设计模型

    3.2 ApplicationBuilder

    3.3 HostingApplication

    3.4 DefaultHttpContextFactory

  • 相关阅读:
    Spring读书笔记
    window.open参数详解
    在spring security3上实现验证码
    Struts2.1 标签详细说明
    总结一下log4j
    【转】hibernate映射oracle自增长
    Java中四舍五入保留七位小数
    求三个整数的最小公倍数
    boxshadow 曲线阴影和翘边阴影
    <div> <p> <span>的用法和区别
  • 原文地址:https://www.cnblogs.com/chenyuxin/p/Http.html
Copyright © 2011-2022 走看看