laravel框架的中间件middleware的详解

2020-08-13 20:17:16
本篇文章给大家带来的内容是关于laravel框架的中间件middleware的详解,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

laravel中间件是个非常方便的东西,能将一些逻辑实现解耦,并且在laravel中,
中间件的编写也是非常的方便。谁用谁知道。

1.装饰器模式

laravel中的中间件使用的就是装饰器模式,什么是[装饰器模式][1],先去了解一下吧,这里大概说一下,就是这个模式主要的就是用于解决 当一个类需要动态扩展功能的时候,使用继承的方式会让子类膨胀,并且这个扩展的功能是个公用功能的情况下,不利于功能的复用以及代码的解耦。

在laravel,使用对于使用这种模式的功能,称为请求处理管道,也就是pipeline

//公共接口interface middleware {        public static function handle(Closure $next);    }//装饰器1class MiddleStepOne implements middleware{        public static function handle(Closure $next) {            echo "前期处理的第一步"."<br>";            $next();            echo "后期处理的第一步"."<br>";        }    }//装饰器2class MiddleStepTwo implements middleware{    public static function handle(Closure $next) {        echo "前期处理的第二步"."<br>";        $next();        echo "后期处理的第二步"."<br>";    }}function goFunc() {    return function ($step,$className) {      return function () use ($step,$className) {          return $className::handle($step);      };    };}$pip = array(    MiddleStepOne::class,    MiddleStepTwo::class,);$pip = array_reverse($pip);  //反转数组,以求达到要求的顺序运行$first = function (){    echo "前期处理完毕"."<br>";};  //实际要处理的函数$a = array_reduce($pip,goFunc(),$first); //遍历pip数组,并将first作为第一个参数传递进去$a(); //执行

输出:

2870495034-5bb6d86103801_articlex.png

这个就是一个简单的基于装饰器模式的管道。他的本质其实就是基于闭包和递归。

通过分析这个程序,对于最终生成的$a变量,它的值大概是这样的 MiddleStepOne.handle(MiddleStepTwo.handle(first)),当执行的时候因为在handle中有个next()函数的存在,所以这是一个递归的调用。对于laravel的中间件,他的实现原理也是和这个一样的。

2.laravel中的中间件和请求处理管道

在laravel中,我们我们可以通过设置中间件来在请求执行之前做一些预先的处理。

从请求入口 public/index.php开始

2545225365-5bb6eae2e4895_articlex.png

重要的是这段代码:即 处理请求,返回请求的响应

$response = $kernel->handle($request = IlluminateHttpRequest::capture() //创建一个请求实例);

接着我们进入kernel中看他的具体实现 IlluminateFoundationHttpKernel.php中

3469362449-5bb6ec5cb66ed_articlex.png

1521463359-5bb6ef37ae916_articlex.png
关于dispatchToRouter()函数请大家自己去看,这里就不多说了。

接下来就是激动人心的PipeLine类了,

<?phpnamespace IlluminatePipeline;use Closure;use RuntimeException;use IlluminateContractsContainerContainer;use IlluminateContractsPipelinePipeline as PipelineContract;class Pipeline implements PipelineContract{    /**     * The container implementation.     *     * @var IlluminateContractsContainerContainer     */    protected $container;    /**     * The object being passed through the pipeline.     *     * @var mixed     */    protected $passable;    /**     * The array of class pipes.     *     * @var array     */    protected $pipes = [];    /**     * The method to call on each pipe.     *     * @var string     */    protected $method = 'handle';    /**     * Create a new class instance.     *     * @param  IlluminateContractsContainerContainer|null  $container     * @return void     */    public function __construct(Container $container = null)    {        $this->container = $container;    }    /**     * Set the object being sent through the pipeline.     *     * @param  mixed  $passable     * @return $this     */    public function send($passable)    {        $this->passable = $passable;        return $this;    }    /**     * Set the array of pipes.     *     * @param  array|mixed  $pipes     * @return $this     */    public function through($pipes)    {        $this->pipes = is_array($pipes) ? $pipes : func_get_args();        return $this;    }    /**     * Set the method to call on the pipes.     *     * @param  string  $method     * @return $this     */    public function via($method)    {        $this->method = $method;        return $this;    }    /**     * 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);    }    /**     * Get the final piece of the Closure onion.     *     * @param  Closure  $destination     * @return Closure     */    protected function prepareDestination(Closure $destination)    {        return function ($passable) use ($destination) {            return $destination($passable);        };    }    /**     * Get a Closure that represents a slice of the application onion.     *     * @return Closure     */    protected function carry()    {        return function ($stack, $pipe) {            return function ($passable) use ($stack, $pipe) {                if (is_callable($pipe)) {                    // If the pipe is an instance of a Closure, we will just call it directly but                    // otherwise we'll resolve the pipes out of the container and call it with                    // the appropriate method and arguments, returning the results back out.                    //如果pip也就中间件函数是一个闭包可调用函数,就直接返回这个闭包函数就行了                    //这里我还没有找到对应的使用场景,后续补充                    return $pipe($passable, $stack);                } elseif (! is_object($pipe)) {                    list($name, $parameters) = $this->parsePipeString($pipe);                    // If the pipe is a string we will parse the string and resolve the class out                    // of the dependency injection container. We can then build a callable and                    // execute the pipe function giving in the parameters that are required.                    $pipe = $this->getContainer()->make($name);                    $parameters = array_merge([$passable, $stack], $parameters);                } else {                    // If the pipe is already an object we'll just make a callable and pass it to                    // the pipe as-is. There is no need to do any extra parsing and formatting                    // since the object we're given was already a fully instantiated object.                    $parameters = [$passable, $stack];                }                return method_exists($pipe, $this->method)                                ? $pipe->{$this->method}(...$parameters)                                : $pipe(...$parameters);            };        };    }    /**     * Parse full pipe string to get name and parameters.     *     * @param  string $pipe     * @return array     */    protected function parsePipeString($pipe)    {        list($name, $parameters) = array_pad(explode(':', $pipe, 2), 2, []);        if (is_string($parameters)) {            $parameters = explode(',', $parameters);        }        return [$name, $parameters];    }    /**     * Get the container instance.     *     * @return IlluminateContractsContainerContainer     * @throws RuntimeException     */    protected function getContainer()    {        if (! $this->container) {            throw new RuntimeException('A container instance has not been passed to the Pipeline.');        }        return $this->container;    }}

总的来说pipeLine类的实现和我之前写的修饰器是差不多,这里主要麻烦的地方就在于就在于

protected function carry()函数内部,对于当pip是闭包,字符串,还有对象的处理。

之前觉得laravel的中间件是个很神秘的东西,但是看了之后才觉得也就那样,很精巧,在实际开发中这种模式也是很有帮助的,例如我们目前用的一个gateway项目,因为没有使用任何框架,所以将判断条件剥离,写入到中间件中, 这样实现了一定程度上的模块化编程。