ThinkPHP框架路由验证器优先于路由中间件执行的解决方案
打个广告先:北京有没有在招PHP的公司,失业中。。。
前段时间在写项目的时候需求是先验证用户是否登录并且有该接口访问权限然后在验证访问参数的合法性,于是在路由内定义了认证中间件以及验证器,结果发现路由验证器执行顺序优先于中间件,当时由于项目急所以就将参数验证写在了控制器里,近期趁着在找新工作的空档期看了一下TP6的路由代码找到一下两个解决方案。
以下内容均采用的ThinkPHP6.0框架,其他版本请根据内容自行判断
方案一:直接修改TP源码进行实现
找到 vendor\topthink\framework\src\think\route\Dispatch.php
文件,修改其 doRouteAfter
和 run
方法,将 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);
}
}
到此整个解决方案就完成了,