中间件

作者: __missing | 来源:发表于2019-05-30 18:24 被阅读0次

前言

对于没有登录的用户重定向到登录页,否则正常请求,想一下对于这个简单的需求你会怎么设计,之前我都是写一个基类,在基类的构造方法中判断是否登录,然后所有的类都继承这个基类,这样写有两个明显的问题1.对于一些不需要登录验证的请求,就得重写构造方法。2.随着这种需求越来越多比如传入的post参数大小的需要限制,这样基类的构造方法会越来越庞大,越来越难维护。(在某一天你就会觉得我怎么会写出这么恶心的代码。。。。)还好laravel提供了中间件。第一次接触laravel时,就被他的中间件深深吸引,可以很方便的给请求加一些额外的处理。今天就来膜拜一下这个神奇的功能是如何实现的。

laravel中间件的执行

中间件是通过pipeline这个管道类执行的(其实路由也是在这里执行的),其中send和through方法只是设置了属性,真正的执行就是then方法了,这个then方法真的是。。。。

<?php
protected function sendRequestThroughRouter($request)
    {
        $middleware = [
            \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
            \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
            \App\Http\Middleware\TrimStrings::class,
            \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
            \App\Http\Middleware\Cors::class,
          ];
          $response = function ($request) {
            $this->app->instance('request', $request);

            return $this->router->dispatch($request);
        };
          return (new Pipeline($this->app))
                    ->send($request)
                    ->through($this->middleware)
                    ->then($response);
    }

then方法中用到了array_reduce函数,

class Pipeline
{
    public function send($passable)
    {
        $this->passable = $passable;
        return $this;
    }

    public function through($pipes)
    {
        $this->pipes = is_array($pipes) ? $pipes : func_get_args();
        return $this;
    }
    public function then(Closure $destination)
    {
        $inital = function ($passable) use($destination) {
                return $destination($passable);
        }

        //这里的array_reverse是为了中间件执行时的顺序和定义时的顺序一致,这里用到了栈的先进后出,等会我们会用一个小实例来说明这个。
        $pipeline = array_reduce(
            array_reverse($this->pipes), $this->carry(), $inital  
        );
        return $pipeline($this->passable);
    }

    public function carry()
    {
        return function ($stack, $pipe) {
            return function ($passable) use ($stack, $pipe) {
                    $slice = this->carry2();
                    $callable = $slice($stack, $pipe);
                    return $callable($passable);
           };
        };
    }

    public function carry2()
    {
        return function ($stack, $pipe) {
            return function ($passable) use ($stack, $pipe) {
                [$name, $parameters] = $this->parsePipeString($pipe); //获取中间件名子和参数
                $pipe = $this->getContainer()->make($name); //获取中间件对象
                $parameters = array_merge([$passable, $stack], $parameters);//组合参数,这个$passable就是我们中间件中平时接受的$request,$stack就是接受的$next

                    $response = method_exists($pipe, 'handle') //如果中间件中存在handel方法就执行handel,如果没有就找中间件中的__invoke方法。
                                ? $pipe->{$this->method}(...$parameters)
                                : $pipe(...$parameters);

                return $response instanceof Responsable
                            ? $response->toResponse($this->getContainer()->make(Request::class))
                            : $response;
            };
        };
    }

}

一个简单的例子

在then方法中,执行完array_reduce这个方法的返回值是一个回调函数,打印结果如下图,注意看经过array_reduce之后最外层的回调函数中的static属性中的item是Test2,数组的最后一个,就是在array_reduce的时候把数组元素递归生成回调,这也就是为什么laravel中要用array_reverse反转一次。


image.png

执行$arr()的过程,($arr就是红色标出的回调函数)首先调用carry中的第二层回调函数,$this->carry2(),调用了carry2中的最外层的回调函数,$test($carry,$item)()执行carry2中的第二层的回调,其中$item是Test2,$carry是第下图中蓝色标出的回调函数,即执行了Test2类中的handel输出‘Test2’,然后执行蓝色标出的回调,会走到Test1的handel,执行黄色标出的回调,Test的handel,执行绿色标出的回调即
function () {
return 'prepareDestination';
};输出Test,Test1,33至此执行完毕。因为表达能力有限说的可能不是很明白建议大家借助xdebug执行这个小demo可能会有更深的理解。

<?php
class Pipine{
    public function then()
    {
        $a = ['test', 'test1','Test2'];
        $prepareDestination = function () {
            return 'prepareDestination';
        };

        $arr = array_reduce($a, $this->carry(), $prepareDestination);
        return $arr();
    }

    public function carry()
    {
        return function($carry, $item) {
            return function () use($carry, $item){
                $test = $this->carry2();
                return $test($carry, $item)();
            };
        };
    }

    public function carry2()
    {
        return function($carry, $item) {
            return function () use($carry, $item){
                $tmp = (new $item)->handel($carry);
                return $tmp;
            };
        };
    }
}

class Test{
    public function handel($carry) {
        $response = $carry();
        echo "Test<br/>";
        return $carry();

    }
}

class Test1 {
    public function handel($carry) {
        $respnse = $carry();
        echo "Test1<br/>";
        return 22;
    }
}

class Test2 {
    public function handel($carry) {
        echo "Test2<br/>";
        $carry();
        return 33;
    }
}

$pipine = new Pipine;

var_dump($pipine->then());
// Test2
// Test
// Test1
// 33

小demo的图解

image.png

相关文章

  • 翻译

    Laravel 的路由中间件 简介 创建中间件 注册中间件全局中间件为路由指定中间件中间件组 中间件参数 Term...

  • 中间件学习——具体分类

    中间件分为远程过程调用中间件、数据访问中间件、消息中间件、事务(交易)处理中间件、分布式对象中间件。 远程过程调用...

  • nodejs19-express中间件

    中间件 匹配路由之前和之后做的操作 应用级中间件 路由级中间件 错误处理中间件 内置中间件 第三方中间件 应用级中...

  • 4.3KOA 中间件模块化与中间件合成

    中间件模块化与中间件合成 一、中间件模块化 定义中间件模块 使用中间件模块 二、使用 koa-compose 模块...

  • 4.2KOA 中间件执行流程

    中间件执行流程 代码执行流程 中间件 1 开始执行中间件 2 开始执行执行内容中间件 2 结束执行中间件 1 结束...

  • 13.中间件和上下文处理器

    中间件 中间件的引入image.png django中的中间件django 中的中间件(middleware),在...

  • Express学习

    使用中间件 Express 应用可使用如下几种中间件:* 应用级中间件* 路由级中间件* 错误处理中间...

  • Express 中间件

    中间件的概念 什么是中间件 中间件(Middleware),特指业务流程的中间处理环节。 Express 中间件的...

  • Scrapy爬虫框架(七) ------ 下载中间件(Midd

    1. Spider 下载中间件(Middleware) Spider 中间件(Middleware) 下载器中间件...

  • laravel api设置Accept 头

    一、创建中间件设置Accept 头 生成中间件 设置Accept头 将中间件添加到api中间件组中

网友评论

      本文标题:中间件

      本文链接:https://www.haomeiwen.com/subject/ylmnaqtx.html