Code前端首页关于Code前端联系我们

什么是 Laravel 中间件?如何找到并使用对应的函数

terry 2年前 (2023-09-25) 阅读数 50 #后端开发

模拟如果我是作者,我会如何实现这个中间件功能,以及如何找到并使用对应的函数。

什么是 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函数时,执行顺序如下:

  1. 先执行log::haddle()方法,
  2. 对于log::-run方法
  3. 默认方法,执行kernel->handle(kernel->handle(request))
  4. 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 '执行顺序如下如下:

  1. 第一次运行Log::haddle(包含csrf::handle闭包函数)方法,
  2. 执行了log::before()方法
  3. 运行闭包意味着csrf::(handle默认)
  4. 执行 csrf::before() 方法
  5. 运行默认方法并执行 kernel->handle(kernel−>handle(request)
  6. 执行 csrf::after❀() 方法 d 'Log::after( ) 方法

注意这里还有一个问题是中间件产生的结果没有被传输,通过改变共享资源也可以达到同样的目的,下一个中间件

就这样了本文档就结束了。其实很多关节我写这篇文章的时候就已经明白了。尤其是对关闭函数的使用和理解更加深入。关闭函数可以延迟资源的使用,比如当前不适合执行的语句必须转发到后面。可以加密并用闭包传递,这是传统函数做不到的。

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门