通过上面的代码我们可以看到,IMiddleware实例是通过IMiddlewareFactory实例创建而来,ASP.NET Core中IMiddlewareFactory默认注册的实现类是MiddlewareFactory,接下来我们看下这个类的实现[点击查看源码👈]
public class MiddlewareFactory : IMiddlewareFactory { private readonly IServiceProvider _serviceProvider; public MiddlewareFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public IMiddleware? Create(Type middlewareType) { //根据类型从容器中获取IMiddleware实例 return _serviceProvider.GetRequiredService(middlewareType) as IMiddleware; } public void Release(IMiddleware middleware) { //因为容器控制了对象的生命周期,所以这里啥也没有 } }
好吧,其实就是在容器中获取的IMiddleware实例,通过这个我们就可以总结出来实现IMiddleware接口的形式创建中间件的操作
需要实现IMiddleware接口,来约束中间件的行为,方法名只能为InvokeAsync
需要手动注册IMiddleware和实现类到容器中,生命周期可自行约束,如果生命周期为Scope或瞬时,那么每次请求都会创建新的中间件实例
没办法通过InvokeAsync方法注入服务,因为受到了IMiddleware接口的约束
上面我们看到了实现IMiddleware接口的方式中间件是如何被初始化的,接下来我们继续来看,基于约定的方式定义的中间件是如何被初始化的。通过上面我们展示的源码可知,实现逻辑在Compile方法中,该方法整体实现方式就是基于Expression,主要原因个人猜测有两点,一个是形式比较灵活能应对的场景较多,二是性能稍微比反射好一点。在此之前,我们先展示一下Compile方法依赖的操作,首先反射是获取UseMiddlewareExtensions类的GetService方法操作
private static readonly MethodInfo GetServiceInfo = typeof(UseMiddlewareExtensions).GetMethod(nameof(GetService), BindingFlags.NonPublic | BindingFlags.Static)!;
其中GetService方法的实现如下所示,其实就是在容器ServiceProvider中获取指定类型实例
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; }
好了上面已将Compile外部依赖已经展示出来了,接下来我们就可以继续探究Compile方法了[点击查看源码👈]
private static Func<T, HttpContext, IServiceProvider, Task> Compile<T>(MethodInfo methodInfo, ParameterInfo[] parameters) { var middleware = typeof(T); //构建三个Parameter名为httpContext、serviceProvider、middleware var httpContextArg = Expression.Parameter(typeof(HttpContext), "httpContext"); var providerArg = Expression.Parameter(typeof(IServiceProvider), "serviceProvider"); var instanceArg = Expression.Parameter(middleware, "middleware"); //穿件Expression数组,且数组第一个参数为httpContextArg var methodArguments = new Expression[parameters.Length]; methodArguments[0] = httpContextArg; //因为Invoke或InvokeAsync方法第一个参数为HttpContext,且methodArguments第一个参数占位,所以跳过第一个参数 for (int i = 1; i < parameters.Length; i++) { //获取方法参数 var parameterType = parameters[i].ParameterType; //不支持ref类型操作 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)) }; //声明调用GetServiceInfo的表达式 var getServiceCall = Expression.Call(GetServiceInfo, parameterTypeExpression); //将getServiceCall操作转换为parameterType methodArguments[i] = Expression.Convert(getServiceCall, parameterType); } //获取中间件类型表达式 Expression middlewareInstanceArg = instanceArg; if (methodInfo.DeclaringType != null && methodInfo.DeclaringType != typeof(T)) { //转换中间件类型表达式类型与声明类型一致 middlewareInstanceArg = Expression.Convert(middlewareInstanceArg, methodInfo.DeclaringType); } //调用middlewareInstanceArg(即当前中间件)的methodInfo(即获取Invoke或InvokeAsync)方法参数(methodArguments) var body = Expression.Call(middlewareInstanceArg, methodInfo, methodArguments); //转换为lambda var lambda = Expression.Lambda<Func<T, HttpContext, IServiceProvider, Task>>(body, instanceArg, httpContextArg, providerArg); return lambda.Compile(); }