本文共 8328 字,大约阅读时间需要 27 分钟。
public class Middleware { private readonly RequestDelegate _next; public RouterMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext httpContext) { // do something await _next.Invoke(httpContext); // do something } }
namespace Microsoft.AspNetCore.Builder.Internal{ public class ApplicationBuilder : IApplicationBuilder { private readonly IList> _components = new List >(); public IApplicationBuilder Use(Func middleware) { _components.Add(middleware); return this; } }}
ApplicationBuilder.Build
方法会处理所有注册的中间件生成的委托集合,将所有中间件生成的委托集合,处理成嵌套的形式,最终得到一个委托,连成一段管道。Reverse
方法将集合反转,从最后注册的中间件对应的委托开始处理Startup.cs
类里面的Configure
方法,里面的每个Use开头的方法都对应一个中间件注册,代码的顺序就是注册的顺序,也是执行的顺序,千万不能写错了。因为MVC处于处理流程的最后面,因此UseMvc方法总是位于最后component
,是从_components
委托集合里面取出来的,执行后又得到一个RequestDelegate
类型的委托,因此由中间件生成的委托的类型应该是Func<RequestDelegate, RequestDelegate>
public RequestDelegate Build() { RequestDelegate app = context => { context.Response.StatusCode = 404; return Task.CompletedTask; }; foreach (var component in _components.Reverse()) { app = component(app); } return app; }
ApplicationBuilder.Use
方法,将中间件生成的委托加入委托集合,完成中间件注册app.Use
方法参数,就是上面需要的类型Func<RequestDelegate, RequestDelegate>
的委托,该委托的参数next
就是下一个中间件对应的委托,返回值就是中间件的Invoke
方法对应的委托,该方法用到了next
public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, params object[] args) { return app.UseMiddleware(typeof(TMiddleware), args); } public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, Type middleware, params object[] args) { // 省略部分代码 var applicationServices = app.ApplicationServices; return app.Use(next => { // 省略部分代码 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
IMiddleware
接口,则调用UseMiddlewareInterface
方法。使用了接口规范,那么你也不能乱写了,只需要注意在Invoke
方法调用next
即可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); } }; }); }
Invoke
或InvokeAsync
的一个方法public static class UseMiddlewareExtensions { internal const string InvokeMethodName = "Invoke"; internal const string InvokeAsyncMethodName = "InvokeAsync"; } 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)); }
Task
var methodinfo = invokeMethods[0]; if (!typeof(Task).IsAssignableFrom(methodinfo.ReturnType)) { throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNonTaskReturnType(InvokeMethodName, InvokeAsyncMethodName, nameof(Task))); }
HttpContext
var parameters = methodinfo.GetParameters(); if (parameters.Length == 0 || parameters[0].ParameterType != typeof(HttpContext)) { throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoParameters(InvokeMethodName, InvokeAsyncMethodName, nameof(HttpContext))); }
UseMiddleware
方法传入的自定义参数args
加上下一个委托next
,得到新的参数数组,然后创建中间件实例,生成Invoke
方法对应委托。此处注意,如果中间件的构造函数中有其它参数,但是未注册到ApplicationServices
的话,需要在UseMiddleware
方法中传入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); }
Compile
方法,生成一个委托,该委托从IServiceProvider
中获取需要的参数的实例,再调用Invoke
方法,相比上面的情况,多了一步从IServiceProvider
获取实例,注入到Invoke
而已。Compile
方法使用了Linq表达式树,源码位于,此处不作讲解,因为我也不太懂var factory = Compile
最后,文章可能有更新,请阅读原文获得更好的体验哦
用心做好每一件事,结果会给你最大的惊喜!转载地址:http://joqwm.baihongyu.com/