# 前言
*****
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驗證器定義](http://www.hmoore.net/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位,也只需要在驗證器中增加一條[驗證規則](http://www.hmoore.net/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+ 中間件](http://www.hmoore.net/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
];
```

最終,在控制器中的代碼我們可以簡化成如下
## 高級寫法
```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 ,我們看到已經自動校驗了!

# 后記
*****
## 開發幫助及交流
如您對本文感興趣想與我聯系交流 您可以
+ 郵件至:xieyongfa@ecarde.cn
+ QQ:2392523899 [點我聊天](http://wpa.qq.com/msgrd?v=3&uin=2392523899&site=qq&menu=yes&from=message&isappinstalled=0)
+ 微信交流
