# 前言
*****
Thinkphp5+ 是一款非常适合API开发的框架,Web安全法则中有非常重要的一条,那就是"永远不要相信用户的输入",所以在接口开发中 一定避免不了数据验证,比如最初级的写法可能是这样子的:
## 初级写法
```php
<?php
namespace app\index\user;
public function register()
{
$request = request();
$params = $request->param();
if (empty($params['user_name'])) {
return json(['code' => -1, 'msg' => '请输入账号']);
}
if (!preg_match('/^[a-z0-9]$/i', $params['user_name'])) {
return json(['code' => -1, 'msg' => '账号只能是英文和数字']);
}
if (empty($params['password'])) {
return json(['code' => -1, 'msg' => '请输入密码']);
}
//todo ...各种判断通过后 再进行实际的业务逻辑
}
```
额,或许我们一个注册接口的判断代码 维护起来都有十几二十行了,万一什么时候 Boss说我们的接口要支持返回xml格式,那岂不是又得加班了? 我们有没有更优雅高效的写法? 当然有, 我们先了解一下 [Thinkphp5.1验证器定义](https://www.kancloud.cn/manual/thinkphp5_1/354102)
我们可以先定义一个验证器
```
namespace app\test\validate;
use think\Validate;
class User extends Validate
protected $rule = [
'user_name' => 'require|alphaNum',
'password' => 'require',
];
protected $message = [
'user_name.require' => '请输入账号',
'user_name.alphaNum' => '账号只能是数字和字母',
'password.require' => '请输入密码',
];
protected $scene = [
'login' => ['user_name', 'password'],
'register' => ['user_name', 'password'],
];
```
然后 我们再控制器中 就可以这样写了
## 中级写法
```php
<?php
namespace app\test\user;
public function register()
{
$request = request();
$params = $request->param();
$validate = new \app\test\validate\User;
if (!$validate->scene('register')->check($params)) {
return json(['code' => -1, 'msg' => $validate->getError()]);
}
//todo ...再进行实际的业务逻辑
}
```
>[warning] 请注意Thinkphp5.1.6+之后的版本才支持中间件。
>
这样 我们就可以不需要在控制器中写一大片的各种判断了,比如 突然要限制账号最多为18位,也只需要在验证器中增加一条[验证规则](https://www.kancloud.cn/manual/thinkphp5_1/354107)就行了,非常灵活!
你以为本文结束了吗? 当然没有,API开发涉及的接口 可能非常多 注册 登录 编辑 等 所以如果我们有多个接口的时候 可能代码是下面这样的
```php
<?php
namespace app\test\user;
public function register()
{
$request = request();
$params = $request->param();
$validate = new \app\test\validate\User;
if (!$validate->scene('register')->check($params)) {
return json(['code' => -1, 'msg' => $validate->getError()]);
}
//todo ...再进行实际的业务逻辑
}
public function login()
{
$request = request();
$params = $request->param();
$validate = new \app\test\validate\User;
if (!$validate->scene('login')->check($params)) {
return json(['code' => -1, 'msg' => $validate->getError()]);
}
//todo ...再进行实际的业务逻辑
}
//还有无数个接口
```
额,看起来并不是很完美优雅,要是有100个接口 岂不是每个方法前面都是雷同相似的代码, 如果有什么办法 能够再执行每个方法之前 都自动判断参数和规则 那就完美了, 接下来我们要出场的是 [Thinkphp5.16+ 中间件](https://www.kancloud.cn/manual/thinkphp5_1/564279),通过阅读官方开发手册,我们了解到了中间件主要用于拦截或过滤应用的HTTP请求,并进行必要的业务处理。那正符合本文中需要解决的场景.
首先我们在 application/test/middleware 目录中创建 中间件文件 Validate.php
```php
<?php
namespace app\test\middleware;
use think\Controller;
class Validate extends Controller
{
/**
* 默认返回资源类型
* @var \think\Request $request
* @var mixed $next
* @var string $name
* @throws \Exception
* @return mixed
*/
public function handle($request, \Closure $next, $name)
{
//获取当前参数
$params = $request->param();
//获取访问模块
$module = $request->module();
//获取访问控制器
$controller = ucfirst($request->controller());
//获取操作名,用于验证场景scene
$scene = $request->action();
$validate = "app\\" . $module . "\\validate\\" . $controller;
//仅当验证器存在时 进行校验
if (class_exists($validate)) {
$v = $this->app->validate($validate);
if ($v->hasScene($scene)) {
//仅当存在验证场景才校验
$result = $this->validate($params, $validate . '.' . $scene);
if (true !== $result) {
//校验不通过则直接返回错误信息
return json(['code' => -1, 'msg' => $result]);
}
}
}
return $next($request);
}
}
```
然后在application\test 目录下 新建middleware.php
```php
<?php
// 中间件扩展定义文件
return [
'validate' => app\test\middleware\Validate::class
];
```
![](https://box.kancloud.cn/f812e9bed5567f384efdd722cc938163_1447x368.png)
最终,在控制器中的代码我们可以简化成如下
## 高级写法
```php
<?php
namespace app\test\user;
public function register()
{
$request = request();
$params = $request->param();
//todo ...此时校验已经通过,可直接进行实际的业务逻辑
}
public function login()
{
$request = request();
$params = $request->param();
//todo ...此时校验已经通过,可直接进行实际的业务逻辑
}
//更多其他接口
```
对的,就是这么神奇, 我们只需要维护 application/test/validate 文件夹中的User.php(必须和控制器文件名完全一致) 文件即可 比如 新增了编辑用户 edit方法,我们只需要在User.php中 $scene 添加 命名为edit(必须和控制器中action方法名完全一致),这样控制器和验证器分离, 代码瞬间简洁很多,维护也变得非常方便了, 让我们测试一下 直接访问127.0.0.1/test/user/login ,我们看到已经自动校验了!
![](https://box.kancloud.cn/98d07fdb1389bba708fbd76db63561d2_617x163.png)
# 后记
*****
## 开发帮助及交流
如您对本文感兴趣想与我联系交流 您可以
+ 邮件至:xieyongfa@ecarde.cn
+ QQ:2392523899 [点我聊天](http://wpa.qq.com/msgrd?v=3&uin=2392523899&site=qq&menu=yes&from=message&isappinstalled=0)
+ 微信交流
![](https://box.kancloud.cn/b74285a950ce81e3cb782f02eb118d59_752x974.jpg =300x389)