路由调度之聚合中间件

未匹配的标注

简介

本篇重点剖析路由中间件如何聚合在一起

聚合概念:从各个路由中间件配置位置合在一起,再从合在一起的单词标识获取中间件类名

主要讲明:

  • 实现中间件聚合逻辑步骤
  • 主要从哪几个中间件配置中聚合起来
  • 控制器的实例化

逻辑简介

  • 1、调用 gatherRouteMiddleware 方法,开始路由中间件聚合
  • 2、在 gatherRouteMiddleware 方法中调用 gatherMiddleware 方法,获取 路由中 和 控制器中 定义的中间件单词标识
  • 3、在 gatherMiddleware 方法中调用 middleware 方法,获取 路由中 定义的中间件单词标识
  • 4、middleware 方法中,主要从匹配好的 Route 对象中 action 属性的 middleware 键获取中间件标识
  • 5、回第4步,接着在 gatherMiddleware 方法中调用 controllerMiddleware 方法,获取 控制器中 定义的中间件单词标识
  • 6、在 controllerMiddleware 方法中调用 isControllerAction 方法,检测 Route 对象中的 action 属性的 uses 键的值是不是字符串。
  • 7、如果检测到不是字符串,返回空数组,即 控制器中 不会设置中间件
  • 8、如果检测到是字符串,则字符串一定是 控制器@动作
  • 9、继续第 8 步,在 controllerMiddleware 方法中调用 controllerDispatcher 获取 控制器调度器 对象
  • 10、调用 控制器调度器 对象中的 getMiddleware 方法,以 控制器对象 和 方法名 为参数
  • 11、回到 controllerMiddleware 方法,调用 getController 方法,获取 控制器对象
  • 12、检测 Route 对象的 controller 属性有没有绑定实例化的 控制器对象
  • 13、如果没有绑定 控制器对象 则在 getController 方法中调用 parseControllerCallback 方法,以 @ 为分隔符,分割 控制器@方法 字符串,取第一个数组元素
  • 14、接着调用 Laravel 容器的 make 方法,开始实例化 控制器对象,最后返回 控制器对象,记住,这里的实例化,会调用 控制器的构造方法,注册 控制器中 的中间件
  • 15、回到第 12 步,如果绑定了 控制器对象,则直接返回 控制器对象
  • 16、回到 controllerMiddleware 方法,调用 getControllerMethod 方法,获取 控制器方法名称
  • 17、在 getControllerMethod 方法中调用 parseControllerCallback ,同样以 @ 为分割符,分割 控制器@方法 字符串,取第二个数组元素
  • 18、回到第 10 步,在 getMiddleware 方法中,检测 控制器对象 有没有 getMiddleware 方法
  • 19、如果没有则返回空数组
  • 20、如果有则调用 控制器对象 中的 getMiddleware 方法,获取第 14 步注册好的中间件单词标识,然后通过集合的 reject 方法过滤掉 except 指定方法,最后返回过滤好的 中间件单词标识
  • 21、回到第 3 步,调用内置 array_unique 和 array_merge 方法,合并 路由中 和 控制器中 配置的中间件单词标识,并去重,最后返回去
  • 22、回到第 2 步,利用集合的 map 方法与 MiddlewareNameResolver 类的 resolve 方法,将中间件单词标识变换成中间件类名
  • 23、在 gatherRouteMiddleware 方法中调用 sortMiddleware 方法,对中间件进行排序,最终返回聚合好的中间件数组

逻辑详解

  • 1、调用 gatherRouteMiddleware 方法,开始路由中间件聚合

    protected function runRouteWithinStack(Route $route, Request $request)
    {
        $shouldSkipMiddleware = $this->container->bound('middleware.disable') &&
                                $this->container->make('middleware.disable') === true;
    
        // 调用 `gatherRouteMiddleware` 方法,开始路由中间件聚合
        $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route);
    
        return (new Pipeline($this->container))
                        ->send($request)
                        ->through($middleware)
                        ->then(function ($request) use ($route) {
                            return $this->prepareResponse(
                                $request, $route->run()
                            );
                        });
    }
  • 2、在 gatherRouteMiddleware 方法中调用 gatherMiddleware 方法,获取 路由中 和 控制器中 定义的中间件单词标识

    public function gatherRouteMiddleware(Route $route)
    {
        // 调用 `gatherMiddleware` 方法,获取 路由中 和 控制器中 定义的中间件单词标识
        $middleware = collect($route->gatherMiddleware())->map(function ($name) {
            return (array) MiddlewareNameResolver::resolve($name, $this->middleware, $this->middlewareGroups);
        })->flatten();
    
        return $this->sortMiddleware($middleware);
    }
  • 3、在 gatherMiddleware 方法中调用 middleware 方法,获取 路由中 定义的中间件单词标识

    public function gatherMiddleware()
    {
        // 检测中间件是否聚合过
        if (! is_null($this->computedMiddleware)) {
            return $this->computedMiddleware;
        }
    
        $this->computedMiddleware = [];
    
        return $this->computedMiddleware = array_unique(array_merge(
            // 没有聚合过,调用 `middleware` 方法,获取 路由中 定义的中间件单词标识
            $this->middleware(), $this->controllerMiddleware()
        ), SORT_REGULAR);
    }
  • 4、middleware 方法中,主要从匹配好的 Route 对象中 action 属性的 middleware 键获取中间件标识

    public function middleware($middleware = null)
    {
        if (is_null($middleware)) {
            // 从匹配好的 Route 对象中 action 属性的 `middleware` 键获取中间件标识
            return (array) ($this->action['middleware'] ?? []);
        }
    
        if (is_string($middleware)) {
            $middleware = func_get_args();
        }
    
        $this->action['middleware'] = array_merge(
            (array) ($this->action['middleware'] ?? []), $middleware
        );
    
        return $this;
    }
  • 5、回第4步,接着在 gatherMiddleware 方法中调用 controllerMiddleware 方法,获取 控制器中 定义的中间件单词标识

    public function gatherMiddleware()
    {
        if (! is_null($this->computedMiddleware)) {
            return $this->computedMiddleware;
        }
    
        $this->computedMiddleware = [];
    
        return $this->computedMiddleware = array_unique(array_merge(
            // 调用 `controllerMiddleware` 方法,获取 控制器中 定义的中间件单词标识
            $this->middleware(), $this->controllerMiddleware()
        ), SORT_REGULAR);
    }
  • 6、在 controllerMiddleware 方法中调用 isControllerAction 方法,检测 Route 对象中的 action 属性的 uses 键的值是不是字符串

  • 7、如何检测到不是字符串,返回空数组,即 控制器中 不会设置中间件

  • 8、如何检测到是字符串,则字符串一定是 控制器@动作

  • 9、继续第 8 步,在 controllerMiddleware 方法中调用 controllerDispatcher 获取 控制器调度器 对象

  • 10、调用 控制器调度器 对象中的 getMiddleware 方法,以 控制器对象 和 方法名 为参数

  • 11、回到 controllerMiddleware 方法,调用 getController 方法,获取 控制器对象

    public function controllerMiddleware()
    {
        // 检测 Route 对象中的 action 属性的 `uses` 键的值是不是字符串
        if (! $this->isControllerAction()) {
            // 如何检测到不是字符串,返回空数组,即 控制器中 不会设置中间件
            return [];
        }
    
        // 调用 `controllerDispatcher` 获取 控制器调度器 对象
        // 调用 控制器调度器 对象中的 getMiddleware 方法,以 控制器对象 和 方法名 为参数
        return $this->controllerDispatcher()->getMiddleware(
            // 调用 getController 方法,获取 控制器对象
            $this->getController(), $this->getControllerMethod()
        );
    }
  • 12、检测 Route 对象的 controller 属性有没有绑定实例化的 控制器对象

  • 13、如果没有绑定 控制器对象 则在 getController 方法中调用 parseControllerCallback 方法,以 @ 为分隔符,分割 控制器@方法 字符串,取第一个数组元素

  • 14、接着调用 Laravel 容器的 make 方法,开始实例化 控制器对象,最后返回 控制器对象,记住,这里的实例化,会调用 控制器的构造方法,注册 控制器中 的中间件

  • 15、回到第 12 步,如果绑定了 控制器对象,则直接返回 控制器对象

    public function getController()
    {
        // 检测 Route 对象的 `controller` 属性有没有绑定实例化的 控制器对象
        if (! $this->controller) {
            // 在 `getController` 方法中调用 `parseControllerCallback` 方法,以 @ 为分隔符,分割 控制器@方法 字符串,取第一个数组元素
            $class = $this->parseControllerCallback()[0];
            // 接着调用 Laravel 容器的 make 方法,开始实例化 控制器对象,最后返回 控制器对象,记住,这里的实例化,会调用 控制器的构造方法,注册 控制器中 的中间件
            $this->controller = $this->container->make(ltrim($class, '\\'));
        }
    
        // 返回 控制器对象
        return $this->controller;
    }
  • 16、回到 controllerMiddleware 方法,调用 getControllerMethod 方法,获取 控制器方法名称

    public function controllerMiddleware()
    {
        if (! $this->isControllerAction()) {
            return [];
        }
    
        return $this->controllerDispatcher()->getMiddleware(
            // 调用 getControllerMethod 方法,获取 控制器方法名称
            $this->getController(), $this->getControllerMethod()
        );
    }
  • 17、在 getControllerMethod 方法中调用 parseControllerCallback ,同样以 @ 为分割符,分割 控制器@方法 字符串,取第二个数组元素

    protected function getControllerMethod()
    {
        // 调用 `parseControllerCallback` ,同样以 @ 为分割符,分割 控制器@方法 字符串,取第二个数组元素
        return $this->parseControllerCallback()[1];
    }
  • 18、回到第 10 步,在 getMiddleware 方法中,检测 控制器对象 有没有 getMiddleware 方法

  • 19、如果没有则返回空数组

  • 20、如果有则调用 控制器对象 中的 getMiddleware 方法,获取第 14 步注册好的中间件单词标识,然后通过集合的 reject 方法过滤掉 except 指定方法,最后返回过滤好的 中间件单词标识

    public function getMiddleware($controller, $method)
    {
        // 检测 控制器对象 有没有 getMiddleware 方法
        if (! method_exists($controller, 'getMiddleware')) {
            return [];
        }
    
        // 调用 控制器对象 中的 getMiddleware 方法,获取第 14 步注册好的中间件单词标识,然后通过集合的 reject 方法过滤掉 except 指定方法,最后返回过滤好的 中间件单词标识
        return collect($controller->getMiddleware())->reject(function ($data) use ($method) {
            return static::methodExcludedByOptions($method, $data['options']);
        })->pluck('middleware')->all();
    }
  • 21、回到第 3 步,调用内置 array_unique 和 array_merge 方法,合并 路由中 和 控制器中 配置的中间件单词标识,并去重,最后返回去

    public function gatherMiddleware()
    {
        if (! is_null($this->computedMiddleware)) {
            return $this->computedMiddleware;
        }
    
        $this->computedMiddleware = [];
    
        // 调用内置 array_unique 和 array_merge 方法,合并 路由中 和 控制器中 配置的中间件单词标识,并去重,最后返回去
        return $this->computedMiddleware = array_unique(array_merge(
            $this->middleware(), $this->controllerMiddleware()
        ), SORT_REGULAR);
    }
  • 22、回到第 2 步,利用集合的 map 方法与 MiddlewareNameResolver 类的 resolve 方法,将中间件单词标识变换成中间件类名

  • 23、在 gatherRouteMiddleware 方法中调用 sortMiddleware 方法,对中间件进行排序,最终返回聚合好的中间件数组

    public function gatherRouteMiddleware(Route $route)
    {
        // 利用集合的 map 方法与 MiddlewareNameResolver 类的 resolve 方法,将中间件单词标识变换成中间件类名
        $middleware = collect($route->gatherMiddleware())->map(function ($name) {
            return (array) MiddlewareNameResolver::resolve($name, $this->middleware, $this->middlewareGroups);
        })->flatten();
    
        // 在 `gatherRouteMiddleware` 方法中调用 `sortMiddleware` 方法,对中间件进行排序,最终返回聚合好的中间件数组
        return $this->sortMiddleware($middleware);
    }

本篇如有错误、不当或者需补充的内容,请各位同僚多提宝贵意见。

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
贡献者:1
讨论数量: 0
发起讨论 查看所有版本


暂无话题~