ThinkPHP框架路由验证器优先于路由中间件执行的解决方案

ThinkPHP框架路由验证器优先于路由中间件执行的解决方案

打个广告先:北京有没有在招PHP的公司,失业中。。。

前段时间在写项目的时候需求是先验证用户是否登录并且有该接口访问权限然后在验证访问参数的合法性,于是在路由内定义了认证中间件以及验证器,结果发现路由验证器执行顺序优先于中间件,当时由于项目急所以就将参数验证写在了控制器里,近期趁着在找新工作的空档期看了一下TP6的路由代码找到一下两个解决方案。

以下内容均采用的ThinkPHP6.0框架,其他版本请根据内容自行判断


方案一:直接修改TP源码进行实现

找到 vendor\topthink\framework\src\think\route\Dispatch.php 文件,修改其 doRouteAfterrun 方法,将 doRouteAfter 方法内的数据验证注释或删除掉,在 run 方法内添加数据验证代码

/**
 * 执行路由调度
 * @access public
 * @return mixed
 */
public function run(): Response
{
	// 数据验证
	$option = $this->rule->getOption();
    if (isset($option['validate'])) {
        $this->autoValidate($option['validate']);
    }
    // run方法内原有代码
    if ($this->rule instanceof RuleItem && $this->request->method() == 'OPTIONS' && $this->rule->isAutoOptions()) {
        $rules = $this->rule->getRouter()->getRule($this->rule->getRule());
        $allow = [];
        foreach ($rules as $item) {
            $allow[] = strtoupper($item->getMethod());
        }

        return Response::create('', 'html', 204)->header(['Allow' => implode(', ', $allow)]);
    }

    $data = $this->exec();
    return $this->autoResponse($data);
}

/**
 * 检查路由后置操作
 * @access protected
 * @return void
 */
protected function doRouteAfter(): void
{
    $option = $this->rule->getOption();

    // 添加中间件
    if (!empty($option['middleware'])) {
        $this->app->middleware->import($option['middleware'], 'route');
    }

    if (!empty($option['append'])) {
        $this->param = array_merge($this->param, $option['append']);
    }

    // 绑定模型数据
    if (!empty($option['model'])) {
        $this->createBindModel($option['model'], $this->param);
    }

    // 记录当前请求的路由规则
    $this->request->setRule($this->rule);

    // 记录路由变量
    $this->request->setRoute($this->param);

    // 数据自动验证
    // if (isset($option['validate'])) {
    //     $this->autoValidate($option['validate']);
    // }
}

方案二:使用TP的容器的功能绑定自定义的 Route 类库

1.使用已经写好的composer扩展

gitee:https://gitee.com/RainLee1990/think-route-patch

$ composer require rainlee/think-route-patch

修改 app 目录下 provider.php 文件,增加键为 think\Route 或 route 值为 \app\route\Route::class 的参数

2.自行创建 Route 及其他相关类库文件

目录结构
www WEB部署目录(或者子目录)
├─app 应用目录
│ ├─route 路由相关类库文件(此文件夹名自定义,如果自定义的文件夹名Copy代码的时候记得修改命名空间)
│ │ ├─dispatch
│ │ │ ├─ Callback.php
│ │ │ ├─ Controller.php
│ │ │ ├─ Url.php
│ │ ├─Domain.php
│ │ ├─Route.php
│ │ ├─RuleGroup.php
│ │ └─RuleItem.php

文件创建完毕后修改 app 目录下 provider.php 文件,增加键为 think\Route 或 route 值为 \app\route\Route::class(根据自己创建的 Route.class 文件的命名空间自行修改) 的参数

Domain.php内容:

<?php
/*
 * @Author: 李红雨 - RainLee <rainlee1990@yeah.net>
 * @Date: 2022-05-19 17:23:49
 * @LastEditors: 李红雨 - RainLee <rainlee1990@yeah.net>
 * @LastEditTime: 2022-05-22 15:50:52
 * @Description: 文件说明
 */

declare(strict_types=1);

namespace app\route;

/**
 * 域名路由
 */
class Domain extends \think\route\Domain
{
    /**
     * 添加分组下的路由规则
     * @access public
     * @param  string $rule   路由规则
     * @param  mixed  $route  路由地址
     * @param  string $method 请求类型
     * @return RuleItem
     */
    public function addRule(string $rule, $route = null, string $method = '*'): RuleItem
    {
        // 读取路由标识
        if (is_string($route)) {
            $name = $route;
        } else {
            $name = null;
        }

        $method = strtolower($method);

        if ('' === $rule || '/' === $rule) {
            $rule .= '$';
        }

        // 创建路由规则实例
        $ruleItem = new RuleItem($this->router, $this, $name, $rule, $route, $method);

        $this->addRuleItem($ruleItem, $method);

        return $ruleItem;
    }
}

Route.php文件内容:

<?php
/*
 * @Author: 李红雨 - RainLee <rainlee1990@yeah.net>
 * @Date: 2022-05-19 17:10:56
 * @LastEditors: 李红雨 - RainLee <rainlee1990@yeah.net>
 * @LastEditTime: 2022-05-22 15:50:29
 * @Description: 文件说明
 */

namespace app\route;

use Closure;

class Route extends \think\Route
{

    /**
     * 初始化默认域名
     * @access protected
     * @return void
     */
    protected function setDefaultDomain(): void
    {
        // 注册默认域名
        $domain = new Domain($this);

        $this->domains['-'] = $domain;

        // 默认分组
        $this->group = $domain;
    }

    /**
     * 注册路由分组
     * @access public
     * @param string|\Closure $name  分组名称或者参数
     * @param mixed           $route 分组路由
     * @return RuleGroup
     */
    public function group($name, $route = null): RuleGroup
    {
        if ($name instanceof Closure) {
            $route = $name;
            $name  = '';
        }

        return (new RuleGroup($this, $this->group, $name, $route))
            ->lazy($this->lazy)
            ->removeSlash($this->removeSlash)
            ->mergeRuleRegex($this->mergeRuleRegex);
    }
}

RuleGroup.php内容:

<?php
/*
 * @Author: 李红雨 - RainLee <rainlee1990@yeah.net>
 * @Date: 2022-05-22 14:01:15
 * @LastEditors: 李红雨 - RainLee <rainlee1990@yeah.net>
 * @LastEditTime: 2022-05-22 14:01:50
 * @Description: 文件说明
 */

namespace app\route;

class RuleGroup extends \think\route\RuleGroup
{
    /**
     * 添加分组下的路由规则
     * @access public
     * @param  string $rule   路由规则
     * @param  mixed  $route  路由地址
     * @param  string $method 请求类型
     * @return RuleItem
     */
    public function addRule(string $rule, $route = null, string $method = '*'): RuleItem
    {
        // 读取路由标识
        if (is_string($route)) {
            $name = $route;
        } else {
            $name = null;
        }

        $method = strtolower($method);

        if ('' === $rule || '/' === $rule) {
            $rule .= '$';
        }

        // 创建路由规则实例
        $ruleItem = new RuleItem($this->router, $this, $name, $rule, $route, $method);

        $this->addRuleItem($ruleItem, $method);

        return $ruleItem;
    }
}

RuleItem.php内容:

<?php
/*
 * @Author: 李红雨 - RainLee <rainlee1990@yeah.net>
 * @Date: 2022-05-21 21:00:26
 * @LastEditors: 李红雨 - RainLee <rainlee1990@yeah.net>
 * @LastEditTime: 2022-05-21 21:01:07
 * @Description: 文件说明
 */

namespace app\route;

use think\Request;
use app\route\dispatch\Callback as CallbackDispatch;
use app\route\dispatch\Controller as ControllerDispatch;

class RuleItem extends \think\route\RuleItem
{
    /**
     * 解析URL地址为 模块/控制器/操作
     * @access protected
     * @param  Request $request Request对象
     * @param  string  $route 路由地址
     * @return CallbackDispatch
     */
    protected function dispatchMethod(Request $request, string $route): CallbackDispatch
    {
        $path = $this->parseUrlPath($route);

        $route  = str_replace('/', '@', implode('/', $path));
        $method = strpos($route, '@') ? explode('@', $route) : $route;

        return new CallbackDispatch($request, $this, $method, $this->vars);
    }

    /**
     * 解析URL地址为 模块/控制器/操作
     * @access protected
     * @param  Request $request Request对象
     * @param  string  $route 路由地址
     * @return ControllerDispatch
     */
    protected function dispatchController(Request $request, string $route): ControllerDispatch
    {
        $path = $this->parseUrlPath($route);

        $action     = array_pop($path);
        $controller = !empty($path) ? array_pop($path) : null;

        // 路由到模块/控制器/操作
        return new ControllerDispatch($request, $this, [$controller, $action], $this->vars);
    }
}

dispatch/Callback.php内容:

<?php
/*
 * @Author: 李红雨 - RainLee <rainlee1990@yeah.net>
 * @Date: 2022-05-19 18:12:23
 * @LastEditors: 李红雨 - RainLee <rainlee1990@yeah.net>
 * @LastEditTime: 2022-05-19 18:33:52
 * @Description: 文件说明
 */

declare(strict_types=1);

namespace app\route\dispatch;

use think\Response;
use think\route\RuleItem;

/**
 * Callback Dispatcher
 */
class Callback extends \think\route\dispatch\Callback
{
    /**
     * 执行路由调度
     * @access public
     * @return mixed
     */
    public function run(): Response
    {
        $option = $this->rule->getOption();
        if (isset($option['validate'])) {
            $this->autoValidate($option['validate']);
        }
        if ($this->rule instanceof RuleItem && $this->request->method() == 'OPTIONS' && $this->rule->isAutoOptions()) {
            $rules = $this->rule->getRouter()->getRule($this->rule->getRule());
            $allow = [];
            foreach ($rules as $item) {
                $allow[] = strtoupper($item->getMethod());
            }

            return Response::create('', 'html', 204)->header(['Allow' => implode(', ', $allow)]);
        }

        $data = $this->exec();
        return $this->autoResponse($data);
    }

    /**
     * 检查路由后置操作
     * @access protected
     * @return void
     */
    protected function doRouteAfter(): void
    {
        $option = $this->rule->getOption();

        // 添加中间件
        if (!empty($option['middleware'])) {
            $this->app->middleware->import($option['middleware'], 'route');
        }

        if (!empty($option['append'])) {
            $this->param = array_merge($this->param, $option['append']);
        }

        // 绑定模型数据
        if (!empty($option['model'])) {
            $this->createBindModel($option['model'], $this->param);
        }

        // 记录当前请求的路由规则
        $this->request->setRule($this->rule);

        // 记录路由变量
        $this->request->setRoute($this->param);
    }
}

dispatch/Controller.php内容:

<?php
/*
 * @Author: 李红雨 - RainLee <rainlee1990@yeah.net>
 * @Date: 2022-05-19 17:12:49
 * @LastEditors: 李红雨 - RainLee <rainlee1990@yeah.net>
 * @LastEditTime: 2022-05-19 18:33:46
 * @Description: 文件说明
 */

declare(strict_types=1);

namespace app\route\dispatch;

use think\Response;
use think\route\RuleItem;

/**
 * Controller Dispatcher
 */
class Controller extends \think\route\dispatch\Controller
{
    /**
     * 执行路由调度
     * @access public
     * @return mixed
     */
    public function run(): Response
    {
        $option = $this->rule->getOption();
        if (isset($option['validate'])) {
            $this->autoValidate($option['validate']);
        }
        if ($this->rule instanceof RuleItem && $this->request->method() == 'OPTIONS' && $this->rule->isAutoOptions()) {
            $rules = $this->rule->getRouter()->getRule($this->rule->getRule());
            $allow = [];
            foreach ($rules as $item) {
                $allow[] = strtoupper($item->getMethod());
            }

            return Response::create('', 'html', 204)->header(['Allow' => implode(', ', $allow)]);
        }

        $data = $this->exec();
        return $this->autoResponse($data);
    }

    /**
     * 检查路由后置操作
     * @access protected
     * @return void
     */
    protected function doRouteAfter(): void
    {
        $option = $this->rule->getOption();

        // 添加中间件
        if (!empty($option['middleware'])) {
            $this->app->middleware->import($option['middleware'], 'route');
        }

        if (!empty($option['append'])) {
            $this->param = array_merge($this->param, $option['append']);
        }

        // 绑定模型数据
        if (!empty($option['model'])) {
            $this->createBindModel($option['model'], $this->param);
        }

        // 记录当前请求的路由规则
        $this->request->setRule($this->rule);

        // 记录路由变量
        $this->request->setRoute($this->param);
    }
}

dispatch/Url .php内容:

<?php
/*
 * @Author: 李红雨 - RainLee <rainlee1990@yeah.net>
 * @Date: 2022-05-19 17:12:49
 * @LastEditors: 李红雨 - RainLee <rainlee1990@yeah.net>
 * @LastEditTime: 2022-05-19 18:33:36
 * @Description: 文件说明
 */

declare(strict_types=1);

namespace app\route\dispatch;

use think\Response;
use think\route\RuleItem;

/**
 * Url Dispatcher
 */
class Url extends \think\route\dispatch\Url
{
    /**
     * 执行路由调度
     * @access public
     * @return mixed
     */
    public function run(): Response
    {
        $option = $this->rule->getOption();
        if (isset($option['validate'])) {
            $this->autoValidate($option['validate']);
        }
        if ($this->rule instanceof RuleItem && $this->request->method() == 'OPTIONS' && $this->rule->isAutoOptions()) {
            $rules = $this->rule->getRouter()->getRule($this->rule->getRule());
            $allow = [];
            foreach ($rules as $item) {
                $allow[] = strtoupper($item->getMethod());
            }

            return Response::create('', 'html', 204)->header(['Allow' => implode(', ', $allow)]);
        }

        $data = $this->exec();
        return $this->autoResponse($data);
    }

    /**
     * 检查路由后置操作
     * @access protected
     * @return void
     */
    protected function doRouteAfter(): void
    {
        $option = $this->rule->getOption();

        // 添加中间件
        if (!empty($option['middleware'])) {
            $this->app->middleware->import($option['middleware'], 'route');
        }

        if (!empty($option['append'])) {
            $this->param = array_merge($this->param, $option['append']);
        }

        // 绑定模型数据
        if (!empty($option['model'])) {
            $this->createBindModel($option['model'], $this->param);
        }

        // 记录当前请求的路由规则
        $this->request->setRule($this->rule);

        // 记录路由变量
        $this->request->setRoute($this->param);
    }
}

到此整个解决方案就完成了,

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ThinkPHP是一个免费开源的,快速、简单的面向对象的轻量级PHP开发框架,遵循Apache2开源协议发布,是为了敏捷WEB应用 开发和简化企业级应用开发而诞生的。拥有众多的优秀功能和特性,经历了三年多发展的同时,在社区团队的积极参与下,在易用性、扩展性和性能方面不断优化和 改进,众多的典型案例确保可以稳定用于商业以及门户级的开发。 ThinkPHP5.1对底层架构做了进一步的改进,减少依赖,其主要特性包括: 采用容统一管理对象 支持Facade 注解路由支持 路由跨域请求支持 配置和路由目录独立 取消系统常量 助手函数增强 类库别名机制 增加条件查询 改进查询机制 配置采用二级 依赖注入完善 支持PSR-3日志规范 中间件支持(V5.1.6+) Swoole/Workerman支持(V5.1.18+) ThinkPHP5的运行环境要求PHP5.6以上,兼容PHP8.0。 更新日志: V5.1.40 LTS(2020-10-09) 本版本为常规更新,主要包括: 改进redis驱动`has`方法 修正XA事务 修正`HasManyThrough`关联 增加mysql json类型字段->>方式获取支持 改进路由加载 避免加载编辑临时文件影响 修复关联模型的属性直接附加到当前模型,当关联模型字段名为name时获取的值为模型的属性name值 修复多态关联预加载`field`无效 改进Collection类的`column`方法的PHP兼容性问题 改进mysql驱动 改进`parseclosure`方法 SoftDelete删除条件做空判断 改进验证类`append`方法

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值