路由调度之聚合中间件

简介

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

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

主要讲明:

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

逻辑简介

  • 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 网站上。
上一篇 下一篇
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 0
发起讨论 只看当前版本


暂无话题~