如何深入解析AspNetCore 7.0源码中的UseMiddleware机制?
- 内容介绍
- 文章标签
- 相关推荐
本文共计2694个文字,预计阅读时间需要11分钟。
使用中间件扩展+前言+本文编写时源代码参考GitHub仓库主分支。aspnetcore+提供了Use方法,供开发者自定义中间件。该方法接收一个委托对象,该委托对象接收一个RequestDelegate对象,并返回一个RequestDelegate对象。
UseMiddlewareExtensions
前言本文编写时源码参考github仓库主分支。
aspnetcore提供了Use方法供开发者自定义中间件,该方法接收一个委托对象,该委托接收一个RequestDelegate对象,并返回一个RequestDelegate对象,方法定义如下:
IApplicationBuilderUse(Func<RequestDelegate,RequestDelegate>middleware);
委托RequestDelegate的定义
///<summary>
///AfunctionthatcanprocessanHTTPrequest.
///</summary>
///<paramname="context">The<seecref="HttpContext"/>fortherequest.</param>
///<returns>Ataskthatrepresentsthecompletionofrequestprocessing.</returns>
publicdelegateTaskRequestDelegate(HttpContextcontext);
如果我们直接使用IApplicationBuilder.Use来写中间件逻辑,可以使用lamda表达式来简化代码,如下:
app.Use((RequestDelegatenext)=>
{
return(HttpContextctx)=>
{
//doyourlogic
returnnext(ctx);
};
});
如果写一些简单的逻辑,这种方式最为方便,问题是如果需要写的中间件代码比较多,依然这样去写,会导致我们Program.cs文件代码非常多,如果有多个中间件,那么最后我们的的Program.cs文件包含多个中间件代码,看上去十分混乱。
为了解决我们上面的代码不优雅,我们希望能将每个中间件业务独立成一个文件,多个中间件代码不混乱的搞到一起。我们需要这样做。
单独的中间件文件
// Middleware1.cs
publicclassMiddleware1
{
publicstaticRequestDelegateLogic(RequestDelegaterequestDelegate)
{
return(HttpContextctx)=>
{
//doyourlogic
returnrequestDelegate(ctx);
};
}
}
调用中间件
app.Use(Middleware1.Logic);
// 以下是其他中间件示例
app.Use(Middleware2.Logic);
app.Use(Middleware3.Logic);
app.Use(Middleware4.Logic);
这种方式可以很好的将各个中间件逻辑独立出来,Program.cs此时变得十分简洁,然而我们还不满足这样,因为我们的Logic方法中直接返回一个lamada表达式(RequestDelegate对象),代码层级深了一层,每个中间件都多写这一层壳似乎不太优雅,能不能去掉这层lamada表达式呢?
为了解决上面提到的痛点,UseMiddlewareExtensions扩展类应运而生,它在Aspnetcore底层大量使用,它主要提供一个泛型UseMiddleware<T>方法用来方便我们注册中间件,下面是该方法的定义
publicstaticIApplicationBuilderUseMiddleware<TMiddleware>(thisIApplicationBuilderapp,paramsobject?[]args)
如果只看这个方法的声明,估计没人知道如何使用,因为该方法接收的泛型参数TMiddleware没有添加任何限制,而另一个args参数也是object类型,而且是可以不传的,也就是它只需要传任意一个类型都不会在编译时报错。
比如这样,完全不会报错:
当然,如果你这样就运行程序,一定会收到下面的异常
System.InvalidOperationException:“No public 'Invoke' or 'InvokeAsync' method found for middleware of type 'System.String'.”
提示我们传的类型没有Invoke或InvokeAsync公共方法,这里大概能猜到,底层应该是通过反射进行动态调用Invoke或InvokeAsync公共方法的。
想要知道其本质,唯有查看源码,以下源码来自UseMiddlewareExtensions
如下,该扩展类一共提供两个并且是重载的公共方法UseMiddleware,一般都只会使用第一个UseMiddleware,第一个UseMiddleware方法内部再去调用第二个UseMiddleware方法,源码中对类型前面添加的[DynamicallyAccessedMembers(MiddlewareAccessibility)]属性可以忽略,它的作用是为了告诉编译器我们通过反射访问的范围,以防止对程序集对我们可能调用的方法或属性等进行裁剪。
internalconststringInvokeMethodName="Invoke";
internalconststringInvokeAsyncMethodName="InvokeAsync";
///<summary>
///Addsamiddlewaretypetotheapplication'srequestpipeline.
///</summary>
///<typeparamname="TMiddleware">Themiddlewaretype.</typeparam>
///<paramname="app">The<seecref="IApplicationBuilder"/>instance.</param>
///<paramname="args">Theargumentstopasstothemiddlewaretypeinstance'sconstructor.</param>
///<returns>The<seecref="IApplicationBuilder"/>instance.</returns>
publicstaticIApplicationBuilderUseMiddleware<[DynamicallyAccessedMembers(MiddlewareAccessibility)]TMiddleware>(thisIApplicationBuilderapp,paramsobject?[]args)
{
returnapp.UseMiddleware(typeof(TMiddleware),args);
}
///<summary>
///Addsamiddlewaretypetotheapplication'srequestpipeline.
///</summary>
///<paramname="app">The<seecref="IApplicationBuilder"/>instance.</param>
///<paramname="middleware">Themiddlewaretype.</param>
///<paramname="args">Theargumentstopasstothemiddlewaretypeinstance'sconstructor.</param>
///<returns>The<seecref="IApplicationBuilder"/>instance.</returns>
publicstaticIApplicationBuilderUseMiddleware(
thisIApplicationBuilderapp,
[DynamicallyAccessedMembers(MiddlewareAccessibility)]Typemiddleware,
paramsobject?[]args)
{
if(typeof(IMiddleware).IsAssignableFrom(middleware))
{
//IMiddlewaredoesn'tsupportpassingargsdirectlysinceit's
//activatedfromthecontainer
if(args.Length>0)
{
thrownewNotSupportedException(Resources.FormatException_UseMiddlewareExplicitArgumentsNotSupported(typeof(IMiddleware)));
}
returnUseMiddlewareInterface(app,middleware);
}
varapplicationServices=app.ApplicationServices;
varmethods=middleware.GetMethods(BindingFlags.Instance|BindingFlags.Public);
MethodInfo?invokeMethod=null;
foreach(varmethodinmethods)
{
if(string.Equals(method.Name,InvokeMethodName,StringComparison.Ordinal)||string.Equals(method.Name,InvokeAsyncMethodName,StringComparison.Ordinal))
{
if(invokeMethodisnotnull)
{
thrownewInvalidOperationException(Resources.FormatException_UseMiddleMutlipleInvokes(InvokeMethodName,InvokeAsyncMethodName));
}
invokeMethod=method;
}
}
if(invokeMethodisnull)
{
thrownewInvalidOperationException(Resources.FormatException_UseMiddlewareNoInvokeMethod(InvokeMethodName,InvokeAsyncMethodName,middleware));
}
if(!typeof(Task).IsAssignableFrom(invokeMethod.ReturnType))
{
thrownewInvalidOperationException(Resources.FormatException_UseMiddlewareNonTaskReturnType(InvokeMethodName,InvokeAsyncMethodName,nameof(Task)));
}
varparameters=invokeMethod.GetParameters();
if(parameters.Length==0||parameters[0].ParameterType!=typeof(HttpContext))
{
thrownewInvalidOperationException(Resources.FormatException_UseMiddlewareNoParameters(InvokeMethodName,InvokeAsyncMethodName,nameof(HttpContext)));
}
varstate=newInvokeMiddlewareState(middleware);
returnapp.Use(next=>
{
varmiddleware=state.Middleware;
varctorArgs=newobject[args.Length+1];
ctorArgs[0]=next;
Array.Copy(args,0,ctorArgs,1,args.Length);
varinstance=ActivatorUtilities.CreateInstance(app.ApplicationServices,middleware,ctorArgs);
if(parameters.Length==1)
{
return(RequestDelegate)invokeMethod.CreateDelegate(typeof(RequestDelegate),instance);
}
varfactory=Compile<object>(invokeMethod,parameters);
returncontext=>
{
varserviceProvider=context.RequestServices??applicationServices;
if(serviceProvider==null)
{
thrownewInvalidOperationException(Resources.FormatException_UseMiddlewareIServiceProviderNotAvailable(nameof(IServiceProvider)));
}
returnfactory(instance,context,serviceProvider);
};
});
}
第一个UseMiddleware可以直接跳过,看第二个UseMiddleware方法,该方法一上来就先判断我们传的泛型类型是不是IMiddleware接口的派生类,如果是,直接交给UseMiddlewareInterface方法。
if(typeof(IMiddleware).IsAssignableFrom(middleware))
{
//IMiddlewaredoesn'tsupportpassingargsdirectlysinceit's
//activatedfromthecontainer
if(args.Length>0)
{
thrownewNotSupportedException(Resources.FormatException_UseMiddlewareExplicitArgumentsNotSupported(typeof(IMiddleware)));
}
returnUseMiddlewareInterface(app,middleware);
}
这里总算看到应该有的东西了,如果声明UseMiddleware<T>方法时,对泛型T添加IMiddleware限制,我们不看源码就知道如何编写我们的中间件逻辑了,只需要写一个类,继承IMiddleware并实现InvokeAsync方法即可, UseMiddlewareInterface方法的实现比较简单,因为我们继承了接口,逻辑相对会简单点。
privatestaticIApplicationBuilderUseMiddlewareInterface(
IApplicationBuilderapp,
TypemiddlewareType)
{
returnapp.Use(next=>
{
returnasynccontext=>
{
varmiddlewareFactory=(IMiddlewareFactory?)context.RequestServices.GetService(typeof(IMiddlewareFactory));
if(middlewareFactory==null)
{
//Nomiddlewarefactory
thrownewInvalidOperationException(Resources.FormatException_UseMiddlewareNoMiddlewareFactory(typeof(IMiddlewareFactory)));
}
varmiddleware=middlewareFactory.Create(middlewareType);
if(middleware==null)
{
//Thefactoryreturnednull,it'sabrokenimplementation
thrownewInvalidOperationException(Resources.FormatException_UseMiddlewareUnableToCreateMiddleware(middlewareFactory.GetType(),middlewareType));
}
try
{
awaitmiddleware.InvokeAsync(context,next);
}
finally
{
middlewareFactory.Release(middleware);
}
};
});
}
publicinterfaceIMiddleware
{
///<summary>
///Requesthandlingmethod.
///</summary>
///<paramname="context">The<seecref="HttpContext"/>forthecurrentrequest.</param>
///<paramname="next">Thedelegaterepresentingtheremainingmiddlewareintherequestpipeline.</param>
///<returns>A<seecref="Task"/>thatrepresentstheexecutionofthismiddleware.</returns>
TaskInvokeAsync(HttpContextcontext,RequestDelegatenext);
}
如果我们的类不满足IMiddleware,继续往下看
通过反射查找泛型类中Invoke或InvokeAsync方法
varapplicationServices=app.ApplicationServices;
varmethods=middleware.GetMethods(BindingFlags.Instance|BindingFlags.Public);
MethodInfo?invokeMethod=null;
foreach(varmethodinmethods)
{
if(string.Equals(method.Name,InvokeMethodName,StringComparison.Ordinal)||string.Equals(method.Name,InvokeAsyncMethodName,StringComparison.Ordinal))
{
// 如果Invoke和InvokeAsync同时存在,则抛出异常,也就是,我们只能二选一
if(invokeMethodisnotnull)
{
thrownewInvalidOperationException(Resources.FormatException_UseMiddleMutlipleInvokes(InvokeMethodName,InvokeAsyncMethodName));
}
invokeMethod=method;
}
}
// 如果找不到Invoke和InvokeAsync则抛出异常,上文提到的那个异常。
if(invokeMethodisnull)
{
thrownewInvalidOperationException(Resources.FormatException_UseMiddlewareNoInvokeMethod(InvokeMethodName,InvokeAsyncMethodName,middleware));
}
// 如果Invoke和InvokeAsync方法的返回值不是Task或Task的派生类,则抛出异常
if(!typeof(Task).IsAssignableFrom(invokeMethod.ReturnType))
{
thrownewInvalidOperationException(Resources.FormatException_UseMiddlewareNonTaskReturnType(InvokeMethodName,InvokeAsyncMethodName,nameof(Task)));
}
Snippet
// 如果Invoke和InvokeAsync方法没有参数,或第一个参数不是HttpContext,抛异常
varparameters=invokeMethod.GetParameters();
if(parameters.Length==0||parameters[0].ParameterType!=typeof(HttpContext))
{
thrownewInvalidOperationException(Resources.FormatException_UseMiddlewareNoParameters(InvokeMethodName,InvokeAsyncMethodName,nameof(HttpContext)));
}
上面一堆逻辑主要就是检查我们的Invoke和InvokeAsync方法是否符合要求,即:必须是接收HttpContext参数,返回Task对象,这恰好就是委托RequestDelegate的定义。
构造RequestDelegate
这部分源码的解读都注释到相应的位置了,如下
varstate=newInvokeMiddlewareState(middleware);
// 调用Use函数,向管道中注册中间件
returnapp.Use(next=>
{
varmiddleware=state.Middleware;
varctorArgs=newobject[args.Length+1];
// next是RequestDelegate对象,作为构造函数的第一个参数传入
ctorArgs[0]=next;
Array.Copy(args,0,ctorArgs,1,args.Length);
// 反射实例化我们传入的泛型类,并把next和args作为构造函数的参数传入
varinstance=ActivatorUtilities.CreateInstance(app.ApplicationServices,middleware,ctorArgs);
// 如果我们的Invoke方法只有一个参数,则直接创建该方法的委托
if(parameters.Length==1)
{
return(RequestDelegate)invokeMethod.CreateDelegate(typeof(RequestDelegate),instance);
}
// 当Invoke方法不止一个参数HttpContext,通过Compile函数创建动态表达式目录树,
// 表达式目录树的构造此处略过,其目的是实现将除第一个参数的其他参数通过IOC注入
varfactory=Compile<object>(invokeMethod,parameters);
returncontext=>
{
// 获取serviceProvider用于在上面构造的表达式目录树中实现依赖注入
varserviceProvider=context.RequestServices??applicationServices;
if(serviceProvider==null)
{
thrownewInvalidOperationException(Resources.FormatException_UseMiddlewareIServiceProviderNotAvailable(nameof(IServiceProvider)));
}
// 将所需的参数传入构造的表达式目录树工厂
returnfactory(instance,context,serviceProvider);
};
});
至此,整个扩展类的源码就解读完了。
通过UseMiddleware注入自定义中间件通过上面的源码解读,我们知道了其实我们传入的泛型类型是有严格的要求的,主要有两种
通过继承IMiddleware
继承IMiddleware并实现该接口的InvokeAsync函数
publicclassMiddleware1:IMiddleware
{
publicasyncTaskInvokeAsync(HttpContextcontext,RequestDelegatenext)
{
//doyourlogic
awaitnext(context);
}
}
通过反射
我们知道,在不继承IMiddleware的情况下,底层会通过反射实例化泛型类型,并通过构造函数传入RequestDelegate,而且要有一个公共函数Invoke或InvokeAsync,并且接收的第一个参数是HttpContext,返回Task,根据要求我们将Middleware1.cs改造如下
publicclassMiddleware1
{
RequestDelegatenext;
publicMiddleware1(RequestDelegatenext)
{
this.next=next;
}
publicasyncTaskInvoke(HttpContexthttpContext)
{
//doyourlogic
awaitthis.next(httpContext);
}
}
总结
通过源码的学习,我们弄清楚底层注册中间件的来龙去脉,两种方式根据自己习惯进行使用,笔者认为通过接口的方式更加简洁直观简单,并且省去了反射带来的性能损失,推荐使用。既然通过继承接口那么爽,为啥还费那么大劲实现反射的方式呢?由源码可知,如果继承接口的话,就不能进行动态传参了。
if(typeof(IMiddleware).IsAssignableFrom(middleware))
{
//IMiddlewaredoesn'tsupportpassingargsdirectlysinceit's
//activatedfromthecontainer
if(args.Length>0)
{
thrownewNotSupportedException(Resources.FormatException_UseMiddlewareExplicitArgumentsNotSupported(typeof(IMiddleware)));
}
returnUseMiddlewareInterface(app,middleware);
}
所以在需要传参的场景,则必须使用反射的方式,所以两种方式都有其存在的必要。
如果本文对您有帮助,还请点赞转发关注一波支持作者。
本文共计2694个文字,预计阅读时间需要11分钟。
使用中间件扩展+前言+本文编写时源代码参考GitHub仓库主分支。aspnetcore+提供了Use方法,供开发者自定义中间件。该方法接收一个委托对象,该委托对象接收一个RequestDelegate对象,并返回一个RequestDelegate对象。
UseMiddlewareExtensions
前言本文编写时源码参考github仓库主分支。
aspnetcore提供了Use方法供开发者自定义中间件,该方法接收一个委托对象,该委托接收一个RequestDelegate对象,并返回一个RequestDelegate对象,方法定义如下:
IApplicationBuilderUse(Func<RequestDelegate,RequestDelegate>middleware);
委托RequestDelegate的定义
///<summary>
///AfunctionthatcanprocessanHTTPrequest.
///</summary>
///<paramname="context">The<seecref="HttpContext"/>fortherequest.</param>
///<returns>Ataskthatrepresentsthecompletionofrequestprocessing.</returns>
publicdelegateTaskRequestDelegate(HttpContextcontext);
如果我们直接使用IApplicationBuilder.Use来写中间件逻辑,可以使用lamda表达式来简化代码,如下:
app.Use((RequestDelegatenext)=>
{
return(HttpContextctx)=>
{
//doyourlogic
returnnext(ctx);
};
});
如果写一些简单的逻辑,这种方式最为方便,问题是如果需要写的中间件代码比较多,依然这样去写,会导致我们Program.cs文件代码非常多,如果有多个中间件,那么最后我们的的Program.cs文件包含多个中间件代码,看上去十分混乱。
为了解决我们上面的代码不优雅,我们希望能将每个中间件业务独立成一个文件,多个中间件代码不混乱的搞到一起。我们需要这样做。
单独的中间件文件
// Middleware1.cs
publicclassMiddleware1
{
publicstaticRequestDelegateLogic(RequestDelegaterequestDelegate)
{
return(HttpContextctx)=>
{
//doyourlogic
returnrequestDelegate(ctx);
};
}
}
调用中间件
app.Use(Middleware1.Logic);
// 以下是其他中间件示例
app.Use(Middleware2.Logic);
app.Use(Middleware3.Logic);
app.Use(Middleware4.Logic);
这种方式可以很好的将各个中间件逻辑独立出来,Program.cs此时变得十分简洁,然而我们还不满足这样,因为我们的Logic方法中直接返回一个lamada表达式(RequestDelegate对象),代码层级深了一层,每个中间件都多写这一层壳似乎不太优雅,能不能去掉这层lamada表达式呢?
为了解决上面提到的痛点,UseMiddlewareExtensions扩展类应运而生,它在Aspnetcore底层大量使用,它主要提供一个泛型UseMiddleware<T>方法用来方便我们注册中间件,下面是该方法的定义
publicstaticIApplicationBuilderUseMiddleware<TMiddleware>(thisIApplicationBuilderapp,paramsobject?[]args)
如果只看这个方法的声明,估计没人知道如何使用,因为该方法接收的泛型参数TMiddleware没有添加任何限制,而另一个args参数也是object类型,而且是可以不传的,也就是它只需要传任意一个类型都不会在编译时报错。
比如这样,完全不会报错:
当然,如果你这样就运行程序,一定会收到下面的异常
System.InvalidOperationException:“No public 'Invoke' or 'InvokeAsync' method found for middleware of type 'System.String'.”
提示我们传的类型没有Invoke或InvokeAsync公共方法,这里大概能猜到,底层应该是通过反射进行动态调用Invoke或InvokeAsync公共方法的。
想要知道其本质,唯有查看源码,以下源码来自UseMiddlewareExtensions
如下,该扩展类一共提供两个并且是重载的公共方法UseMiddleware,一般都只会使用第一个UseMiddleware,第一个UseMiddleware方法内部再去调用第二个UseMiddleware方法,源码中对类型前面添加的[DynamicallyAccessedMembers(MiddlewareAccessibility)]属性可以忽略,它的作用是为了告诉编译器我们通过反射访问的范围,以防止对程序集对我们可能调用的方法或属性等进行裁剪。
internalconststringInvokeMethodName="Invoke";
internalconststringInvokeAsyncMethodName="InvokeAsync";
///<summary>
///Addsamiddlewaretypetotheapplication'srequestpipeline.
///</summary>
///<typeparamname="TMiddleware">Themiddlewaretype.</typeparam>
///<paramname="app">The<seecref="IApplicationBuilder"/>instance.</param>
///<paramname="args">Theargumentstopasstothemiddlewaretypeinstance'sconstructor.</param>
///<returns>The<seecref="IApplicationBuilder"/>instance.</returns>
publicstaticIApplicationBuilderUseMiddleware<[DynamicallyAccessedMembers(MiddlewareAccessibility)]TMiddleware>(thisIApplicationBuilderapp,paramsobject?[]args)
{
returnapp.UseMiddleware(typeof(TMiddleware),args);
}
///<summary>
///Addsamiddlewaretypetotheapplication'srequestpipeline.
///</summary>
///<paramname="app">The<seecref="IApplicationBuilder"/>instance.</param>
///<paramname="middleware">Themiddlewaretype.</param>
///<paramname="args">Theargumentstopasstothemiddlewaretypeinstance'sconstructor.</param>
///<returns>The<seecref="IApplicationBuilder"/>instance.</returns>
publicstaticIApplicationBuilderUseMiddleware(
thisIApplicationBuilderapp,
[DynamicallyAccessedMembers(MiddlewareAccessibility)]Typemiddleware,
paramsobject?[]args)
{
if(typeof(IMiddleware).IsAssignableFrom(middleware))
{
//IMiddlewaredoesn'tsupportpassingargsdirectlysinceit's
//activatedfromthecontainer
if(args.Length>0)
{
thrownewNotSupportedException(Resources.FormatException_UseMiddlewareExplicitArgumentsNotSupported(typeof(IMiddleware)));
}
returnUseMiddlewareInterface(app,middleware);
}
varapplicationServices=app.ApplicationServices;
varmethods=middleware.GetMethods(BindingFlags.Instance|BindingFlags.Public);
MethodInfo?invokeMethod=null;
foreach(varmethodinmethods)
{
if(string.Equals(method.Name,InvokeMethodName,StringComparison.Ordinal)||string.Equals(method.Name,InvokeAsyncMethodName,StringComparison.Ordinal))
{
if(invokeMethodisnotnull)
{
thrownewInvalidOperationException(Resources.FormatException_UseMiddleMutlipleInvokes(InvokeMethodName,InvokeAsyncMethodName));
}
invokeMethod=method;
}
}
if(invokeMethodisnull)
{
thrownewInvalidOperationException(Resources.FormatException_UseMiddlewareNoInvokeMethod(InvokeMethodName,InvokeAsyncMethodName,middleware));
}
if(!typeof(Task).IsAssignableFrom(invokeMethod.ReturnType))
{
thrownewInvalidOperationException(Resources.FormatException_UseMiddlewareNonTaskReturnType(InvokeMethodName,InvokeAsyncMethodName,nameof(Task)));
}
varparameters=invokeMethod.GetParameters();
if(parameters.Length==0||parameters[0].ParameterType!=typeof(HttpContext))
{
thrownewInvalidOperationException(Resources.FormatException_UseMiddlewareNoParameters(InvokeMethodName,InvokeAsyncMethodName,nameof(HttpContext)));
}
varstate=newInvokeMiddlewareState(middleware);
returnapp.Use(next=>
{
varmiddleware=state.Middleware;
varctorArgs=newobject[args.Length+1];
ctorArgs[0]=next;
Array.Copy(args,0,ctorArgs,1,args.Length);
varinstance=ActivatorUtilities.CreateInstance(app.ApplicationServices,middleware,ctorArgs);
if(parameters.Length==1)
{
return(RequestDelegate)invokeMethod.CreateDelegate(typeof(RequestDelegate),instance);
}
varfactory=Compile<object>(invokeMethod,parameters);
returncontext=>
{
varserviceProvider=context.RequestServices??applicationServices;
if(serviceProvider==null)
{
thrownewInvalidOperationException(Resources.FormatException_UseMiddlewareIServiceProviderNotAvailable(nameof(IServiceProvider)));
}
returnfactory(instance,context,serviceProvider);
};
});
}
第一个UseMiddleware可以直接跳过,看第二个UseMiddleware方法,该方法一上来就先判断我们传的泛型类型是不是IMiddleware接口的派生类,如果是,直接交给UseMiddlewareInterface方法。
if(typeof(IMiddleware).IsAssignableFrom(middleware))
{
//IMiddlewaredoesn'tsupportpassingargsdirectlysinceit's
//activatedfromthecontainer
if(args.Length>0)
{
thrownewNotSupportedException(Resources.FormatException_UseMiddlewareExplicitArgumentsNotSupported(typeof(IMiddleware)));
}
returnUseMiddlewareInterface(app,middleware);
}
这里总算看到应该有的东西了,如果声明UseMiddleware<T>方法时,对泛型T添加IMiddleware限制,我们不看源码就知道如何编写我们的中间件逻辑了,只需要写一个类,继承IMiddleware并实现InvokeAsync方法即可, UseMiddlewareInterface方法的实现比较简单,因为我们继承了接口,逻辑相对会简单点。
privatestaticIApplicationBuilderUseMiddlewareInterface(
IApplicationBuilderapp,
TypemiddlewareType)
{
returnapp.Use(next=>
{
returnasynccontext=>
{
varmiddlewareFactory=(IMiddlewareFactory?)context.RequestServices.GetService(typeof(IMiddlewareFactory));
if(middlewareFactory==null)
{
//Nomiddlewarefactory
thrownewInvalidOperationException(Resources.FormatException_UseMiddlewareNoMiddlewareFactory(typeof(IMiddlewareFactory)));
}
varmiddleware=middlewareFactory.Create(middlewareType);
if(middleware==null)
{
//Thefactoryreturnednull,it'sabrokenimplementation
thrownewInvalidOperationException(Resources.FormatException_UseMiddlewareUnableToCreateMiddleware(middlewareFactory.GetType(),middlewareType));
}
try
{
awaitmiddleware.InvokeAsync(context,next);
}
finally
{
middlewareFactory.Release(middleware);
}
};
});
}
publicinterfaceIMiddleware
{
///<summary>
///Requesthandlingmethod.
///</summary>
///<paramname="context">The<seecref="HttpContext"/>forthecurrentrequest.</param>
///<paramname="next">Thedelegaterepresentingtheremainingmiddlewareintherequestpipeline.</param>
///<returns>A<seecref="Task"/>thatrepresentstheexecutionofthismiddleware.</returns>
TaskInvokeAsync(HttpContextcontext,RequestDelegatenext);
}
如果我们的类不满足IMiddleware,继续往下看
通过反射查找泛型类中Invoke或InvokeAsync方法
varapplicationServices=app.ApplicationServices;
varmethods=middleware.GetMethods(BindingFlags.Instance|BindingFlags.Public);
MethodInfo?invokeMethod=null;
foreach(varmethodinmethods)
{
if(string.Equals(method.Name,InvokeMethodName,StringComparison.Ordinal)||string.Equals(method.Name,InvokeAsyncMethodName,StringComparison.Ordinal))
{
// 如果Invoke和InvokeAsync同时存在,则抛出异常,也就是,我们只能二选一
if(invokeMethodisnotnull)
{
thrownewInvalidOperationException(Resources.FormatException_UseMiddleMutlipleInvokes(InvokeMethodName,InvokeAsyncMethodName));
}
invokeMethod=method;
}
}
// 如果找不到Invoke和InvokeAsync则抛出异常,上文提到的那个异常。
if(invokeMethodisnull)
{
thrownewInvalidOperationException(Resources.FormatException_UseMiddlewareNoInvokeMethod(InvokeMethodName,InvokeAsyncMethodName,middleware));
}
// 如果Invoke和InvokeAsync方法的返回值不是Task或Task的派生类,则抛出异常
if(!typeof(Task).IsAssignableFrom(invokeMethod.ReturnType))
{
thrownewInvalidOperationException(Resources.FormatException_UseMiddlewareNonTaskReturnType(InvokeMethodName,InvokeAsyncMethodName,nameof(Task)));
}
Snippet
// 如果Invoke和InvokeAsync方法没有参数,或第一个参数不是HttpContext,抛异常
varparameters=invokeMethod.GetParameters();
if(parameters.Length==0||parameters[0].ParameterType!=typeof(HttpContext))
{
thrownewInvalidOperationException(Resources.FormatException_UseMiddlewareNoParameters(InvokeMethodName,InvokeAsyncMethodName,nameof(HttpContext)));
}
上面一堆逻辑主要就是检查我们的Invoke和InvokeAsync方法是否符合要求,即:必须是接收HttpContext参数,返回Task对象,这恰好就是委托RequestDelegate的定义。
构造RequestDelegate
这部分源码的解读都注释到相应的位置了,如下
varstate=newInvokeMiddlewareState(middleware);
// 调用Use函数,向管道中注册中间件
returnapp.Use(next=>
{
varmiddleware=state.Middleware;
varctorArgs=newobject[args.Length+1];
// next是RequestDelegate对象,作为构造函数的第一个参数传入
ctorArgs[0]=next;
Array.Copy(args,0,ctorArgs,1,args.Length);
// 反射实例化我们传入的泛型类,并把next和args作为构造函数的参数传入
varinstance=ActivatorUtilities.CreateInstance(app.ApplicationServices,middleware,ctorArgs);
// 如果我们的Invoke方法只有一个参数,则直接创建该方法的委托
if(parameters.Length==1)
{
return(RequestDelegate)invokeMethod.CreateDelegate(typeof(RequestDelegate),instance);
}
// 当Invoke方法不止一个参数HttpContext,通过Compile函数创建动态表达式目录树,
// 表达式目录树的构造此处略过,其目的是实现将除第一个参数的其他参数通过IOC注入
varfactory=Compile<object>(invokeMethod,parameters);
returncontext=>
{
// 获取serviceProvider用于在上面构造的表达式目录树中实现依赖注入
varserviceProvider=context.RequestServices??applicationServices;
if(serviceProvider==null)
{
thrownewInvalidOperationException(Resources.FormatException_UseMiddlewareIServiceProviderNotAvailable(nameof(IServiceProvider)));
}
// 将所需的参数传入构造的表达式目录树工厂
returnfactory(instance,context,serviceProvider);
};
});
至此,整个扩展类的源码就解读完了。
通过UseMiddleware注入自定义中间件通过上面的源码解读,我们知道了其实我们传入的泛型类型是有严格的要求的,主要有两种
通过继承IMiddleware
继承IMiddleware并实现该接口的InvokeAsync函数
publicclassMiddleware1:IMiddleware
{
publicasyncTaskInvokeAsync(HttpContextcontext,RequestDelegatenext)
{
//doyourlogic
awaitnext(context);
}
}
通过反射
我们知道,在不继承IMiddleware的情况下,底层会通过反射实例化泛型类型,并通过构造函数传入RequestDelegate,而且要有一个公共函数Invoke或InvokeAsync,并且接收的第一个参数是HttpContext,返回Task,根据要求我们将Middleware1.cs改造如下
publicclassMiddleware1
{
RequestDelegatenext;
publicMiddleware1(RequestDelegatenext)
{
this.next=next;
}
publicasyncTaskInvoke(HttpContexthttpContext)
{
//doyourlogic
awaitthis.next(httpContext);
}
}
总结
通过源码的学习,我们弄清楚底层注册中间件的来龙去脉,两种方式根据自己习惯进行使用,笔者认为通过接口的方式更加简洁直观简单,并且省去了反射带来的性能损失,推荐使用。既然通过继承接口那么爽,为啥还费那么大劲实现反射的方式呢?由源码可知,如果继承接口的话,就不能进行动态传参了。
if(typeof(IMiddleware).IsAssignableFrom(middleware))
{
//IMiddlewaredoesn'tsupportpassingargsdirectlysinceit's
//activatedfromthecontainer
if(args.Length>0)
{
thrownewNotSupportedException(Resources.FormatException_UseMiddlewareExplicitArgumentsNotSupported(typeof(IMiddleware)));
}
returnUseMiddlewareInterface(app,middleware);
}
所以在需要传参的场景,则必须使用反射的方式,所以两种方式都有其存在的必要。
如果本文对您有帮助,还请点赞转发关注一波支持作者。

