什么是 Laravel 中间件?如何找到并使用对应的函数
模拟如果我是作者,我会如何实现这个中间件功能,以及如何找到并使用对应的函数。
什么是 Laravel 中间件
Laravel 中间件提供了一种机制,可以在不改变逻辑代码的情况下中断原有程序流程,通过中间件处理一些事件或扩展一些功能。例如,日志中间件可以轻松记录请求和响应日志,而无需更改逻辑代码。
那么我们来简化一下软件的执行过程。现在有了一个核心类core,下面是它的Laravel代码
$request= Illuminate\Http\Request::capture()$response=$kernel->handle($request);
代码的功能是完成一个请求并返回一个响应。下面是分配给具体执行逻辑并返回结果的代码段。
那么如果你想在 $kernel->handle() 方法执行之前或之后添加一段逻辑,你会怎么写呢?大致如下:
$request= Illuminate\Http\Request::capture()functionmidware(){before()$response=$kernel->handle($request);after()}
当然这样写是没有问题的,但是没有可扩展性。您需要修改此方法才能执行某些操作。不可能将其封装在框架的核心内容中。如何改进?
定义一个执行的中间件类,称为中间件。该类实现了before()和after()两个方法,代码如下。
middleware ='';$request= Illuminate\Http\Request::capture()functionmidware(){
middleware.before()$response=$kernel->handle($request);
middleware.after()}
它能解决问题吗?改变并不能解决问题。但是如果我们需要多个中间件怎么办?最容易想到的是:定义一个中间件数组middleware_arr。每个中间件类都包含before和after方法,代码如下:
middleware_arr=array();$request= Illuminate\Http\Request::capture()functionmidware(){foreach(middleware_arr as middleware){
middleware.before()}$response=$kernel->handle($request);foreach(middleware_arr as middleware){
middleware.after()}}
虽然有点老套,但是解决了问题。但是这样还有一个问题,那就是我们如何向中间件传递参数,所以如下是没问题的:
$request= Illuminate\Http\Request::capture()functionmidware(){foreach(middleware_arr as middleware){
middleware.before($request)}$response=$kernel->handle($request);foreach(middleware_arr as middleware){
middleware.after($response)}}
看似解决了问题,但是仔细分析就会发现,每一个有中间件的时候,就是原来的$请求,显然是不行的。改成如下:
$request= Illuminate\Http\Request::capture()functionmidware(){foreach(middleware_arr as middleware){$request= middleware.before($request)}$response=$kernel->handle($request);foreach(middleware_arr as middleware){$response= middleware.after($response)}}
还有一个问题是,假设有两个中间件A和B,那么执行顺序应该是怎样的:
$request= Illuminate\Http\Request::capture()$request= A.before($request);$request= B.before($request);$response=$kernel->handle($request);$response= A.after();$response= B.after();
这样合理吗?不,这很容易说。我们假设有一个注册请求和响应日志的中间件。这时候无论你放在哪里,都无法完美记录最初的请求和最后的日志。在类似的情况下是否有必要编写两个类,一个用于接收请求并将其放在中间件数组中的第一个类,另一个用于处理响应并将其放在数组的最后一个?最好在执行后续的foreach之前将middleware_arr数组反转一下,这样就满足要求了:
$request= Illuminate\Http\Request::capture()$request= A.before($request);$request= B.before($request);$response=$kernel->handle($request);$response= B.after();$response= A.after();
但我也开始怀疑这种老式的、不灵活的方案是否还有更好的解决方案。观察这个执行顺序后,我检查了一下,发现是包风格(洋葱风格)。我们能否为下一个问题找到更灵活、更优雅的解决方案?看着上面的结构,总感觉有点眼熟。这与A的函数包装B的函数的方式非常相似,B的函数包含初始执行代码。函数内调用函数很容易,但是这里的每个中间件都不知道对方的存在,所以另一个中间件执行的函数必须转发到上层。这里使用了 close 函数和 PHP 函数。 array_reduce(),
array_reduce函数定义:mixed array_reduce(array $input, callable $function [,mixed $initial = NULL])
<?phpfunction rsum ($v,$w){$v+=$w;return$v;}function rmul ($v,$w){$v*=$w;return$v;}$a=array(1,2,3,4,5);$x=array();$b= array_reduce ($a,"rsum");$c= array_reduce ($a,"rmul",10);?>
这将使 $b 的值为 15, $c 的值为 1200(=10*1*2*3*4*5)
array_reduce()调用输入数组函数回调各个单元。 ,从而将数组减少为单个值。我们将多个函数包装到一个名为finally 的函数中。
$middleware_arr=['log'];$default=function()use($request){return$kernel->handle($request);}$callback=array_reduce($middleware_arr,function($stack,$pipe){returnfunction()use($stack,$pipe){return$pipe::handle($stack);};},$default);function()use($default,$log){return$log::handle($default);};classlogimplementsMilldeware{publicstaticfunctionhandle(Closure $func){$func();}}classlogimplementsMilldeware{publicstaticfunctionhandle(Closure $func){$func();}}
这样,执行call函数时,执行顺序如下:
- 先执行log::haddle()方法,
- 对于log::-run方法
- 默认方法,执行kernel->handle(kernel->handle(request))
- Run log::after()方法
然后模拟不同情况如下:
$middleware_arr=['csrf','log'];$default=function()use($request){return$kernel->handle($request);}$callback=array_reduce($middleware_arr,function($stack,$pipe){returnfunction()use($stack,$pipe){return$pipe::handle($stack);};},$default);$log::handle(function()use($default,$csrf){return$csrf::handle($default);});
D '执行顺序如下如下:
- 第一次运行Log::haddle(包含csrf::handle闭包函数)方法,
- 执行了log::before()方法
- 运行闭包意味着csrf::(handle默认)
- 执行 csrf::before() 方法
- 运行默认方法并执行 kernel->handle(kernel−>handle(request)
- 执行 csrf::after❀() 方法 d 'Log::after( ) 方法
注意这里还有一个问题是中间件产生的结果没有被传输,通过改变共享资源也可以达到同样的目的,下一个中间件
就这样了本文档就结束了。其实很多关节我写这篇文章的时候就已经明白了。尤其是对关闭函数的使用和理解更加深入。关闭函数可以延迟资源的使用,比如当前不适合执行的语句必须转发到后面。可以加密并用闭包传递,这是传统函数做不到的。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。