什么是laravel中间件?怎么找到并使用对应的函数

2020-03-2910:35:45后端程序开发Comments2,125 views字数 4116阅读模式

模拟了如果我是作者,我是怎么实现这个中间件功能,又是怎么找到并使用对应的函数。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/17978.html

什么是laravel中间件

Laravel 中间件提供了一种机制在不修改逻辑代码的情况下,中断原本程序流程,通过中间件来处理一些事件,或者扩展一些功能。比如日志中间件可以方便的记录请求和响应日志,而不需要去更改逻辑代码。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/17978.html

那么我们简化一下软件执行过程,现在有一个核心类kernel,下面是它的laravel代码文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/17978.html

$request= Illuminate\Http\Request::capture()$response=$kernel->handle($request);

代码的作用是 捕获一个 Request ,返回一个 Response。这里面就是后续分发到具体执行逻辑的代码段并返回结果。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/17978.html

那么如果想在执行这个$kernel->handle()方法之前或者之后,增加一段逻辑一般会怎么写呢。大概如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/17978.html

$request= Illuminate\Http\Request::capture()functionmidware(){before()$response=$kernel->handle($request);after()}

显然这样写没有问题,但是毫无拓展性可言,想执行什么东西都要更改这个方法,这种是不可能封装成框架核心内容的。怎么改进呢文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/17978.html

定义一个要执行的中间件类叫middleware,类实现两个方法,before()和after()然后代码如下。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/17978.html


middleware ='';$request= Illuminate\Http\Request::capture()functionmidware(){
    middleware.before()$response=$kernel->handle($request);
    middleware.after()}

是否解决了问题呢,是解决了不用更改的问题,但是我们如果需要多个中间件怎么办呢,最容易想到的就是:定义一个中间件数组middleware_arr,每一个middleware类都含有before和after方法,代码如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/17978.html


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()}}

虽然有点老土,但是的确解决了问题。但是这个还存在一个问题,就是我们怎么向中间件传递参数的问题,那么如下可以吗:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/17978.html

$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,这显然不行,修改成如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/17978.html

$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,那么执行顺序应该是怎么样呢:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/17978.html

$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数组给反转一下,这样就符合了要求:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/17978.html

$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(),文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/17978.html

array_reduce函数定义:mixed array_reduce ( array $input , callable $function [, mixed $initial = NULL ] )文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/17978.html

<?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() 将回调函数 function 迭代地作用到 input 数组中的每一个单元中,从而将数组简化为单一的值。咱们是把多个函数包裹成最终调用一个函数。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/17978.html

$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();}}

这样在执行callback函数的时候,执行顺序如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/17978.html

  1. 先运行log::haddle()方法,
  2. 执行了log::before()方法
  3. 运行default方法,执行kernel->handle(request)
  4. 运行log::after()方法

然后模拟多个的情况如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/17978.html

$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);});

执行顺序如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/17978.html

  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()方法

注意这里还有一个问题就是中间件产生的结果,并没有进行传递,可以通过修改共有资源的方式来达到相同的目的,并非需要真的传值到下一个中间件。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/17978.html

到此这篇文件就结束了,其实其中很多关节都是我写这篇文章的时候才想明白的。尤其是对闭包函数的运用和理解更深了,闭包函数可以延迟利用资源,比如当前不适合执行的语句,又要传递到后面,利用闭包可以封装起来传递出去,这是传统函数做不到的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/17978.html

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/bc/17978.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定