laravel 中间件实现原理

2 篇文章 0 订阅
2 篇文章 0 订阅
最近在学习laravel框架的时候,发现框架实现了中间件,闲着没事就顺藤摸瓜,研究了其实现原理,有分析不对的请留言指正。
百度百科解读中间件

中间件是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的。目前,它并没有很严格的定义,但是普遍接受IDC的定义:中间件是一种独立的系统软件服务程序,分布式应用软件借助这种软件在不同的技术之间共享资源,中间件位于客户机服务器的操作系统之上,管理计算资源和网络通信。从这个意义上可以用一个等式来表示中间件:中间件=平台+通信,这也就限定了只有用于分布式系统中才能叫中间件,同时也把它与支撑软件和实用软件区分开来

Laravel解读中间件?

中间件为过滤进入应用的 HTTP 请求提供了一套便利的机制。例如,Laravel 内置了一个中间件来验证用户是否经过认证(如登录),如果用户没有经过认证,中间件会将用户重定向到登录页面,而如果用户已经经过认证,中间件就会允许请求继续往前进入下一步操作。
当然,除了认证之外,中间件还可以被用来处理很多其它任务。比如:CORS 中间件可以用于为离开站点的响应添加合适的头(跨域);日志中间件可以记录所有进入站点的请求,从而方便我们构建系统日志系统。
Laravel 框架自带了一些中间件,包括认证、CSRF 保护中间件等等。所有的中间件都位于 app/Http/Middleware 目录下

个人解读

中间件主要是处理行为前的逻辑,比如权限验证,数据过滤,黑名单验证等,可以避免过多的if else语句出现在处理业务逻辑的地方,进行行为验证与业务逻辑处理分离,使得整体的代码结构看上去更加优雅更加清晰。

首先是源码跟踪 (入口文件\project\public\index.php)
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);
$response->send();
执行 ( Illuminate\Foundation\Http\Kernel.php)
 public function handle($request)
    {
        try {
            $request->enableHttpMethodParameterOverride();
            $response = $this->sendRequestThroughRouter($request);
        } catch (Throwable $e) {
            $this->reportException($e);

            $response = $this->renderException($request, $e);
        }

        $this->app['events']->dispatch(
            new RequestHandled($request, $response)
        );

        return $response;
    }
继而执行 sendRequestThroughRouter 方法

所有的请求都是在这进行处理,注意Pipeline对象,类似于Linux里面的管道符|,此类主要进行请求之前过滤的操作,如中间件的调用,可以看出这个方法支持链式调用,通过send方法设置管道发送的对象,
through方法设置是否需要进行中间件操作,而这些中间件的加载通过 path\app\Http\Kernel.php 来进行配置。

/**
     * Send the given request through the middleware / router.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    protected function sendRequestThroughRouter($request)
    {
        $this->app->instance('request', $request);

        Facade::clearResolvedInstance('request');

        $this->bootstrap();
        return (new Pipeline($this->app))
                    ->send($request)
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());
    }
最后通过then方法进行执行,代码如下

这段代码看起来比较简单,深入了解之后发现还是比较绕,表示卡在这里比较久,脑子一直转不过来,可能比较笨,哈哈,当然 在这里花费的时间比较长,之后在我会文本末端写一个例子来说明此处方法调用逻辑



    //$this->pipes() 是一个数组 代码如下
	protected $pipes = [
         \App\Http\Middleware\TrustHosts::class,
        \App\Http\Middleware\TrustProxies::class,
        \Fruitcake\Cors\HandleCors::class,
        \App\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    ];
   /**
     * Run the pipeline with a final destination callback.
     *
     * @param  \Closure  $destination
     * @return mixed
     */
    public function then(Closure $destination)
    {
        $pipeline = array_reduce(
           array_reverse($this->pipes()), $this->carry(), $this->prepareDestination($destination)
        );
        return $pipeline($this->passable);
    }
	
    //carry 方法 去掉没必要的代码,以免影响阅读和理解
    public function carry()
    {
    	return function ($stack, $pipe) {
            return function ($passable) use ($stack, $pipe) {
                try {
                	//$this->method="handle"
                	 $parameters = [$passable, $stack];
                    return method_exists($pipe, $this->method)
                                    ? $pipe->{$this->method}(...$parameters)
                                    : $pipe(...$parameters);;
                } catch (Throwable $e) {
                    return $this->handleException($passable, $e);
                }
            };
        };
    }
	//最后执行的方法体,是一个闭包(不了解闭包的同学,了解后才能继续阅读哦),不然都不知道这是干嘛用的,
	public function prepareDestination($destination)
	{
		return function ($passable) use ($destination) {
            try {
                return $destination($passable);
            } catch (Throwable $e) {
                return $this->handleException($passable, $e);
            }
        };
	}
再次解读then方法
		 $pipeline = array_reduce(
          		array_reverse($this->pipes()),
          		 $this->carry(),
          		  $this->prepareDestination($destination)
       		);
     		return $pipeline($this->passable);

这里会生成一个循环堆调用,结构如下所示
![![在这里插入图片描述](https://img-blog.csdnimg.cn/20200813165311659.png?x-oss-

为保证每个方法都能调用到必须以如下方式返回,否则只会调用到第一个方法的时候直接结束,导致请求中断,结果会直接返回。
	public function handle(Request $request, $next)
    {
    	//@todo 业务逻辑处理
    	//$request 请求体 $next下一个处理的方法逻辑,也就上图所画的调用逻辑
        return $next($request);
    }

到这里 整个中间件的实现原理已分析完毕,主要还是掌握闭包堆栈处理,这也是laravel处理中间件的主要逻辑方法,理解起来还是比较麻烦的,在这里我给大家准备了个例子,方便打印调试(laravel中调试简直就是一场噩梦,浏览器直接奔溃)。

例子(复制即用)

class handdle{

    public function go($a,$b)
    {
        echo "我进来了哦go".PHP_EOL;


        return $b($a); //如果这里不这样调用,程序就到此结束
    }
}

class handdleQuestion{
    public function go($a,$b)
    {
        echo "我进来了哦handdleQuestion".PHP_EOL;
        return $b($a); //如果这里不这样调用,程序就到此结束
        return true;
    }
}


function test(){
    return function ($stack, $pipe) {
       // echo "return ".$pipe.PHP_EOL;
        return function ($passable) use ($stack, $pipe) {
           // print_r($stack);die;
            $parameters = [$passable, $stack];
            return $pipe->go(...$parameters);
        };
    };
}


function call_back(Closure $destination){
    return function ($passable) use ($destination) {
        return $destination($passable);
    };
}


$obj = function($a){  //这是最后执行方法
    echo "哈哈哈".rand().PHP_EOL;
    var_dump($a); //不出意外这里会打印$passable对象
};

$pipeline = array_reduce(
    [new handdle(),new handdleQuestion()], test(), call_back($obj)
);
$passable = new stdClass();
$passable->id = 100;
$pipeline($passable);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值