众所周知,TP(thinkphp简称)中存在验证器,但需要你自己调用,因而有没有办法可以不用每个action中都写上验证器的代码呢??
这里需要引入中间件(middleware)的概念:
中间件主要用于拦截或过滤应用的
HTTP请求,并进行必要的业务处理。1
在TP中中间件是处理中间流程,或者一些通用流程(比如:接口需要写入访问日志)时,需要调用或者执行的代码块。
关于如何自动验证,我们应该有如下思路:
有一个中间件来调用验证器,在这个中间件中必须能知道访问路径。
根据访问路径来调用对应的验证器类。
根据访问路径对应的action来判断验证器是否存在场景(scene)
是否验证成功
验证失败抛出异常
创建Validate中间件
因此我们可以创建一个Validate的中间件,用于过滤一部分错误的请求。(毕竟连参数都不正确,我的接口怎么会给你调用呢?)
//在终端中输入
# php think make:middleware Validate
// 此时你可以得到一个名叫Validate的middleware。
# Validate.php文件
<?php
namespace app\middleware;
class Validate
{
public function handel($request, $next){
}
}获取请求路径并分析
如果是TP5,我们大可以使用如下代码来获取请求路径:
$m = $request->module(true); // 获取请求模块名
$c = $request->controller(true); // 获取请求控制器名
$a = $request->action(true); // 获取请求方法名但是TP6取消了module方法,且在应用初始化前无法获得contoller和action的名称。
class Validate extends Middleware{
public function handel($request, $next){
//前置事件
// todo code
echo $request->controller(); #输出''
$resp = $next($request); //这一步会执行action,所以验证不能在之后执行。
//后置事件
// todo code
echo $request->controller(); #输出'index'
return $resp;
}
}
因此无法调用TP的方法,这时候该怎么办呢??
经过查阅Tp文档,发现Request2中有:
这个方法,但这个方法中含有url后缀,(既.html或者.htm,等等),因此我们可以看到这个方法:
因此我们可以使用这行代码来获取请求路径:
$path = $request->ext()
? substr($request->pathinfo(), 0, -(strlen($request->ext()) + 1))
: $request->pathinfo();这样得到了请求路径,比如:you.domain.com/index/test/aaa的访问中我们就得到了index/test/aaa这个字符串,可以使用explode函数来根据'/'切割字符串。
$path = $request->ext()
? substr($request->pathinfo(), 0, -(strlen($request->ext()) + 1))
: $request->pathinfo();
$namePath = explode( '/', $path);正常情况下$namePath是一个长度为3的字符串数组,但当你的请求路径如you.domain.com/index/test时,我们得到的路径就是index/test,而如果请求路径仅仅只有域名时,我们得到的路径可能是一个空字符串。所以我们需要做判断,默认有3层,请看下面代码。
/** 路径解析
* @param string $className 基础类名 比如: app\\Validate\\
* @param array $namePath 经过上面分割得字符串数组
* @param string $action 需要调用得action名字
* @return string 验证器类名
*/
private function pathParse(string $className,array $namePath,string &$action): string{
$model = (isset($namePath[0]) && !$namePath[0]) ? $namePath[0] : 'index';
$className .= $model . '\\';
$controller = (isset($namePath[1]) && !$namePath[1]) ? $namePath[1] : 'index';
$className .= Str::studly($controller);
$action = (isset($namePath[2]) && !$namePath[2]) ? $namePath[2] : 'index';
return $className;
}如此我们就获取到对应模块的验证器类了。
调用验证器并验证参数
经过上述思路,我们成功拿到了验证器类名了。
不过万一这个接口不需要验证,不久没有验证器了么??
那我们需要判断这个验证器是否存在。
存在时则开始执行验证,否则可以跳过验证器步骤。
$className = pathParse($className, $namePath, $action);
if (class_exists($className)) {
/**
* @var $valid \think\Validate
*/
$valid = new $className();
if ($valid->hasScene($action) && !$valid->scene($action)->check(input())) {
$error = $valid->getError();
return response($error,200, [], 'json');
}
}接下来就贴上完成代码:
<?php
namespace app\middleware;
use app\Request;
use think\helper\Str;
use think\Middleware;
class Validate extends Middleware
{
public function handle(Request $request, \Closure $next)
{
$path = $request->ext()
? substr($request->pathinfo(), 0, -(strlen($request->ext()) + 1))
: $request->pathinfo();
$className = $this->getValidateClassName($path, $action);
if (class_exists($className)) {
/**
* @var $valid \think\Validate
*/
$valid = new $className();
if ($valid->hasScene($action) && !$valid->scene($action)->check(input())) {
$error = $valid->getError();
return response($error,200, [], 'json');
}
}
return $next($request);
}
public function getValidateClassName($path, &$action): string
{
$namePath = explode( '/', $path);
$className = 'app\\validate\\';
if (count($namePath) > 3) {
$className = $this->deepPathParse($className, $namePath, $action);
}else{
$className = $this->shallowPathParse($className, $namePath, $action);
}
return $className . 'Validate';
}
/**
* 深度路径解析
* @param string $className 初始类名
* @param array $namePath 类名路径
* @param string $action 最终执行方法
* @return string
*/
private function deepPathParse(string $className, array $namePath, string &$action): string {
$controller = $namePath[count($namePath) - 2];
$action = $namePath[count($namePath) - 1];
foreach ($namePath as $name) {
if ($name == $action || $name == $controller) {
continue;
}
$className .= $name . '\\';
}
$className .= Str::studly($controller);
return $className;
}
/**
* 浅路径解析
* @param string $className
* @param array $namePath
* @param string $action
* @return string
*/
private function shallowPathParse(string $className,array $namePath,string &$action): string{
$model = (isset($namePath[0]) && !$namePath[0]) ? $namePath[0] : 'index';
$className .= $model . '\\';
$controller = (isset($namePath[1]) && !$namePath[1]) ? $namePath[1] : 'index';
$className .= Str::studly($controller);
$action = (isset($namePath[2]) && !$namePath[2]) ? $namePath[2] : 'index';
return $className;
}
}感谢: