首页后端开发PHPlaravel中间件如何创建?浅析l中间件的使用

laravel中间件如何创建?浅析l中间件的使用

时间2024-03-26 16:22:03发布访客分类PHP浏览1099
导读:这篇文章给大家分享的是laravel中间件创建的相关内容,本文对大家学习和理解中间件的使用有一定的帮助,文中示例代码介绍的非常详细,感兴趣的朋友接下来一起跟随小编看看吧。Laravel 中间件提供了一种机制在不修改逻辑代码的情况下,中断原本...

这篇文章给大家分享的是laravel中间件创建的相关内容,本文对大家学习和理解中间件的使用有一定的帮助,文中示例代码介绍的非常详细,感兴趣的朋友接下来一起跟随小编看看吧。

Laravel 中间件提供了一种机制在不修改逻辑代码的情况下,中断原本程序流程,通过中间件来处理一些事件,或者扩展一些功能。比如日志中间件可以方便的记录请求和响应日志,而不需要去更改逻辑代码。

那么我们简化一下软件执行过程,现在有一个核心类kernel,下面是它的laravel代码

#捕获请求
$request = Illuminate\Http\Request::capture()
#处理请求
$response = $kernel->
    handle($request);
    

代码的作用是 捕获一个 Request ,返回一个 Response。这里面就是后续分发到具体执行逻辑的代码段并返回结果。

那么如果想在执行这个$kernel-> handle()方法之前或者之后,增加一段逻辑一般会怎么写呢。大概如下:

$request = Illuminate\Http\Request::capture()
function midware(){
    
    before()#在之前执行的语句集合
    #####   
    $response = $kernel->
    handle($request);

    #####
    after()#在之后执行的语句集合
 
}
    

显然这样写没有问题,但是毫无拓展性可言,想执行什么东西都要更改这个方法,这种是不可能封装成框架核心内容的。怎么改进呢

定义一个要执行的中间件类叫middleware,类实现两个方法,before()和after()然后代码如下。

#配置项中有一项配置中间件:
middleware = '';

$request = Illuminate\Http\Request::capture()
function midware(){
    
    middleware.before()
    #####   
    $response = $kernel->
    handle($request);

    #####
    middleware.after()
}
    

是否解决了问题呢,是解决了不用更改的问题,但是我们如果需要多个中间件怎么办呢,最容易想到的就是:定义一个中间件数组middleware_arr,每一个middleware类都含有before和after方法,代码如下:

配置项中有middleware_arr
middleware_arr=array();

$request = Illuminate\Http\Request::capture()
function midware(){

    foreach(middleware_arr as middleware){

       middleware.before()
    }
    
    #####   
    $response = $kernel->
    handle($request);

    #####
    foreach(middleware_arr as middleware){

        middleware.after()
    }

}

虽然有点老土,但是的确解决了问题。但是这个还存在一个问题,就是我们怎么向中间件传递参数的问题,那么如下可以吗:

$request = Illuminate\Http\Request::capture()
function midware(){

    foreach(middleware_arr as middleware){

       middleware.before($request)
    }
    
    #####   
    $response = $kernel->
    handle($request);

    #####
    foreach(middleware_arr as middleware){

        middleware.after($response)
    }

}

看似是解决了问题,但是仔细分析,就会发现,这里面每次给中间件的都是最初的$request,这显然不行,修改成如下:

$request = Illuminate\Http\Request::capture()
function midware(){

    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的函数包括了最初的执行代码。函数内部调用函数容易,但是咱们这里每一个中间件之间是不知道对方存在的,所以要把其他中间件要执行的函数传递到上一级,这里就用到了闭包函数还有一个php函数array_reduce(),

array_reduce函数定义:mixed array_reduce ( array $input , callable $function [, mixed $initial = NULL ] )

?php
function  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() 将回调函数 function 迭代地作用到 input 数组中的每一个单元中,从而将数组简化为单一的值。咱们是把多个函数包裹成最终调用一个函数。

#我们先假设只有一个middleware,叫log来简化情况,这里的类应该是一个类全路径,我这里就简单的写一下,要不然太长了。
$middleware_arr = ['log'];

#最终要执行的代码先封装成一个闭包,要不然没有办法传递到内层,如果用函数名传递函数的话,是没有办法传递参数的。
$default = function() use($request){
    
    return $kernel->
    handle($request);

}

$callback = array_reduce($middleware_arr,function($stack,$pipe) {

    return function() use($stack,$pipe){
    
        return $pipe::handle($stack);

    }
    ;

}
    ,$default);

# 这里 callback最终是 这样一个函数:
function() use($default,$log){
    
    return $log::handle($default);

}
    ;

#所以每一个中间件都需要有一个方法handle方法,方法中要对传输的函数进行运行,类似如下,这里我类名就不大写了
class log implements Milldeware {

    public static function handle(Closure $func){
    
        $func();

    }

}

#这里不难看出可以加入中间件自身逻辑如下:
class log implements Milldeware {

    public static function handle(Closure $func){
    
        #这里可以运行逻辑块before()
        $func();

        #这里可以运行逻辑块after()
    }

}
    

这样在执行callback函数的时候,执行顺序如下:

先运行log::haddle()方法,

执行了log::before()方法

运行default方法,执行$kernel-> handle($request)

运行log::after()方法

然后模拟多个的情况如下:

$middleware_arr = ['csrf','log'];

#最终要执行的代码先封装成一个闭包,要不然没有办法传递到内层,如果用函数名传递函数的话,是没有办法传递参数的。
$default = function() use($request){
    
    return $kernel->
    handle($request);

}

$callback = array_reduce($middleware_arr,function($stack,$pipe) {

    return function() use($stack,$pipe){
    
        return $pipe::handle($stack);

    }
    ;

}
    ,$default);


# 这里 callback最终是 执行这样:
$log::handle(function() use($default,$csrf){
    
    return $csrf::handle($default);

}
    );
    

执行顺序如下:

1.先运行log::haddle(包含csrf::handle闭包函数)方法,

2.执行了log::before()方法

3.运行闭包也就是运行了$csrf::handle($default)

4.执行了csrf::before()方法

5.运行default方法,执行$kernel-> handle($request)

6.执行了csrf::after()方法

7.运行log::after()方法

注意这里还有一个问题就是中间件产生的结果,并没有进行传递,可以通过修改共有资源的方式来达到相同的目的,并非需要真的传值到下一个中间件。

关于laravel中间件创建的内容就介绍到这,本文对laravel中间件的使用有很详细的介绍,需要的朋友可以参考了解看看,希望能对大家有帮助,想要了解更多laravel框架的内容,大家可以关注其它的相关文章。

文本转载自脚本之家

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!


若转载请注明出处: laravel中间件如何创建?浅析l中间件的使用
本文地址: https://pptw.com/jishu/653616.html
GO不支持循环引用是什么原因呢? 使用laravel的服务容器有哪些好处?三个示例带你了解

游客 回复需填写必要信息