本文主要是对. NET Core 开发日志 --Middleware 的补遗, 但是会从看起来平平无奇的 RequestDelegate 开始叙述, 所以以其作为标题, 也是合情合理.
RequestDelegate 是一种委托类型, 其全貌为
public delegate Task RequestDelegate(HttpContext context)
,MSDN 上对它的解释,"A function that can process an HTTP request."-- 处理 HTTP 请求的函数. 唯一参数, 是最熟悉不过的 HttpContext, 返回值则是表示请求处理完成的异步操作类型.
可以将其理解为 ASP.NET Core 中对一切 HTTP 请求处理的抽象 (委托类型本身可视为函数模板, 其实现具有统一的参数列表及返回值类型), 没有它整个框架就失去了对 HTTP 请求的处理能力.
并且它也是构成 Middleware 的基石. 或者更准确地说参数与返回值都是其的
Func<RequestDelegate, RequestDelegate>
委托类型正是维持 Middleware 运转的核心齿轮.
组装齿轮的地方位于 ApplicationBuilder 类之内, 其中包含着所有齿轮的集合.
private readonly IList<Func<RequestDelegate, RequestDelegate>> _components = new List<Func<RequestDelegate, RequestDelegate>>();
以及添加齿轮的方法:
- public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
- {
- _components.Add(middleware);
- return this;
- }
在 Startup 类的 Configure 方法里调用以上 ApplicationBuilder 的 Use 方法, 就可以完成一个最简单的 Middleware.
- public void Configure(IApplicationBuilder app)
- {
- app.Use(_ =>
- {
- return context =>
- {
- return context.Response.WriteAsync("Hello, World!");
- };
- });
- }
齿轮要想变成 Middleware, 在完成添加后, 还需要经过组装.
- public RequestDelegate Build()
- {
- RequestDelegate app = context =>
- {
- context.Response.StatusCode = 404;
- return Task.CompletedTask;
- };
- foreach (var component in _components.Reverse())
- {
- app = component(app);
- }
- return app;
- }
Build 方法里先定义了最底层的零件 --app,
context => { context.Response.StatusCode = 404; return Task.CompletedTask; }
, 这段代码意味着, 如果没有添加任何 Middleware 的话, ASP.NET Core 站点启动后, 会直接出现 404 的错误.
接下的一段, 遍历倒序排列的齿轮, 开始正式组装.
在上述例子里, 只使用了一个齿轮:
- _ =>
- {
- return context =>
- {
- return context.Response.WriteAsync("Hello, World!");
- };
- }
那么第一次也是最后一次循环后, 执行 component(app) 操作, app 被重新赋值为:
context => context.Response.WriteAsync("Hello, World!");
组装的结果便是 app 的值.
这个组装过程在 webHost 进行 BuildApplication 时开始操作. 从此方法的返回值类型可以看出, 虽然明义上是创建 Application, 其实生成的是 RequestDelegate.
- private RequestDelegate BuildApplication()
- {
- try
- {
- ...
- var builderFactory = _applicationServices.GetRequiredService<IApplicationBuilderFactory>();
- var builder = builderFactory.CreateBuilder(Server.Features);
- ...
- Action<IApplicationBuilder> configure = _startup.Configure;
- ...
- configure(builder);
- return builder.Build();
- }
- ...
- }
而这个 RequestDelegate 最终会在 HostingApplication 类的 ProcessRequestAsync 方法里被调用.
- public virtual async Task StartAsync(CancellationToken cancellationToken = default)
- {
- ...
- var application = BuildApplication();
- ...
- var hostingApp = new HostingApplication(application, _logger, diagnosticSource, httpContextFactory);
- ...
- }
- public HostingApplication(
- RequestDelegate application,
- ILogger logger,
- DiagnosticListener diagnosticSource,
- IHttpContextFactory httpContextFactory)
- {
- _application = application;
- _diagnostics = new HostingApplicationDiagnostics(logger, diagnosticSource);
- _httpContextFactory = httpContextFactory;
- }
- public Task ProcessRequestAsync(Context context)
- {
- return _application(context.HttpContext);
- }
上例中的执行结果即是显示 Hello, World! 字符.
404 的错误不再出现, 意味着这种 Middleware 只会完成自己对 HTTP 请求的处理, 并不会将请求传至下一层的 Middleware.
要想达成不断传递请求的目的, 需要使用另一种 Use 扩展方法.
- public static IApplicationBuilder Use(this IApplicationBuilder app, Func<HttpContext, Func<Task>, Task> middleware)
- {
- return app.Use(next =>
- {
- return context =>
- {
- Func<Task> simpleNext = () => next(context);
- return middleware(context, simpleNext);
- };
- });
- }
在实际代码中可以这么写:
- public void Configure(IApplicationBuilder app)
- {
- app.Use(async (context, next) =>
- {
- await context.Response.WriteAsync("I am a Middleware!\n");
- await next.Invoke();
- });
- app.Use(_ =>
- {
- return context =>
- {
- return context.Response.WriteAsync("Hello, World!");
- };
- });
- }
现在多了个 Middleware, 继续上面的组装过程. app 的值最终被赋值为:
- async context =>
- {
- Func<Task> simpleNext = () => context.Response.WriteAsync("Hello, World!");
- await context.Response.WriteAsync("I am a Middleware!\n");
- await simpleNext.Invoke();
- };
显示结果为:
- I am a Middleware!
- Hello, World!
下面的流程图中可以清楚地说明这个过程.
如果把
await next.Invoke()
注释掉的话,
- public void Configure(IApplicationBuilder app)
- {
- app.Use(async (context, next) =>
- {
- await context.Response.WriteAsync("I am a Middleware!\n");
- //await next.Invoke();
- });
- app.Use(_ =>
- {
- return context =>
- {
- return context.Response.WriteAsync("Hello, World!");
- };
- });
- }
上例中第一个 Middleware 处理完后, 不会继续交给第二个 Middleware 处理. 注意以下 simpleNext 的方法只被定义而没有被调用.
- async context =>
- {
- Func<Task> simpleNext = () => context.Response.WriteAsync("Hello, World!");
- await context.Response.WriteAsync("I am a Middleware!\n");
- };
这种情况被称为短路 (short-circuiting).
做短路处理的 Middleware 一般会放在所有 Middleware 的最后, 以作为整个 pipeline 的终点.
并且更常见的方式是用 Run 扩展方法.
- public static void Run(this IApplicationBuilder app, RequestDelegate handler)
- {
- ...
- app.Use(_ => handler);
- }
所以可以把上面例子的代码改成下面的形式:
- public void Configure(IApplicationBuilder app)
- {
- app.Use(async (context, next) =>
- {
- await context.Response.WriteAsync("I am a Middleware!\n");
- await next.Invoke();
- });
- app.Run(async context =>
- {
- await context.Response.WriteAsync("Hello, World!");
- });
- }
除了短路之外, Middleware 处理时还可以有分支的情况.
- public void Configure(IApplicationBuilder app)
- {
- app.Map("/branch1", ab => {
- ab.Run(async context =>
- {
- await context.Response.WriteAsync("Map branch 1");
- });
- });
- app.Map("/branch2", ab => {
- ab.Run(async context =>
- {
- await context.Response.WriteAsync("Map branch 2");
- });
- });
- app.Use(async (context, next) =>
- {
- await context.Response.WriteAsync("I am a Middleware!\n");
- await next.Invoke();
- });
- app.Run(async context =>
- {
- await context.Response.WriteAsync("Hello, World!");
- });
- }
URL 地址后面跟着 branch1 时:
URL 地址后面跟着 branch2 时:
其它情况下:
Map 扩展方法的代码实现:
- public static IApplicationBuilder Map(this IApplicationBuilder app, PathString pathMatch, Action<IApplicationBuilder> configuration)
- {
- ...
- // create branch
- var branchBuilder = app.New();
- configuration(branchBuilder);
- var branch = branchBuilder.Build();
- var options = new MapOptions
- {
- Branch = branch,
- PathMatch = pathMatch,
- };
- return app.Use(next => new MapMiddleware(next, options).Invoke);
- }
创建分支的办法就是重新实例化一个 ApplicationBuilder.
- public IApplicationBuilder New()
- {
- return new ApplicationBuilder(this);
- }
对分支的处理则是封装在 MapMiddleware 类之中.
- public async Task Invoke(HttpContext context)
- {
- ...
- PathString matchedPath;
- PathString remainingPath;
- if (context.Request.Path.StartsWithSegments(_options.PathMatch, out matchedPath, out remainingPath))
- {
- // Update the path
- var path = context.Request.Path;
- var pathBase = context.Request.PathBase;
- context.Request.PathBase = pathBase.Add(matchedPath);
- context.Request.Path = remainingPath;
- try
- {
- await _options.Branch(context);
- }
- finally
- {
- context.Request.PathBase = pathBase;
- context.Request.Path = path;
- }
- }
- else
- {
- await _next(context);
- }
- }
说到 MapMiddleware, 不得不提及各种以 Use 开头的扩展方法, 比如 UseStaticFiles,UseMvc,UsePathBase 等等.
这些方法内部都会调用 UseMiddleware 方法以使用各类定制的 Middleware 类. 如下面 UsePathBase 的代码:
- public static IApplicationBuilder UsePathBase(this IApplicationBuilder app, PathString pathBase)
- {
- ...
- // Strip trailing slashes
- pathBase = pathBase.Value?.TrimEnd('/');
- if (!pathBase.HasValue)
- {
- return app;
- }
- return app.UseMiddleware<UsePathBaseMiddleware>(pathBase);
- }
而从 UseMiddleware 方法中可以获知, Middleware 类需满足两者条件之一才能被有效使用. 其一是实现 IMiddleware, 其二, 必须有 Invoke 或者 InvokeAsync 方法, 且方法至少要有一个 HttpContext 类型参数 (它还只能是放第一个), 同时返回值需要是 Task 类型.
- internal const string InvokeMethodName = "Invoke";
- internal const string InvokeAsyncMethodName = "InvokeAsync";
- public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, Type middleware, params object[] args)
- {
- if (typeof(IMiddleware).GetTypeInfo().IsAssignableFrom(middleware.GetTypeInfo()))
- {
- ...
- 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();
- ...
- 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;
- ...
- return factory(instance, context, serviceProvider);
- };
- });
- }
对 ASP.NET Core 中 Middleware 的介绍到此终于可以告一段落, 希望这两篇文章能够为读者提供些许助力.
来源: https://www.cnblogs.com/kenwoo/p/9404671.html