FastAdmin 完整实现前后端表单验证(自动加载 Validate 验证器)
FastAdmin CRUD 表单双重验证教程 | 开启 modelValidate 自动调用 Validate 验证器,解决前端 data-rule 失效问题。
目录
[二、步骤 1:创建 Validate 验证器](#二、步骤 1:创建 Validate 验证器)
[三、步骤 2:控制器开启自动验证](#三、步骤 2:控制器开启自动验证)
[四、步骤 3:视图页面添加前端辅助校验(add.html/edit.html)](#四、步骤 3:视图页面添加前端辅助校验(add.html/edit.html))
[坑 1:验证器类名、命名空间和模型不一致](#坑 1:验证器类名、命名空间和模型不一致)
[坑 2:忘记开启 modelSceneValidate = true](#坑 2:忘记开启 modelSceneValidate = true)
[坑 3:规则写成 between1,5](#坑 3:规则写成 between[1,5])
[坑 4:前端 data-rule 正常,关闭 JS 后非法数据可以提交](#坑 4:前端 data-rule 正常,关闭 JS 后非法数据可以提交)
[坑 5:修改代码后验证不生效](#坑 5:修改代码后验证不生效)
[坑 6:字段名称不匹配](#坑 6:字段名称不匹配)
前言
很多 FastAdmin 开发者只使用前端data-rule表单校验,但前端 JS 验证可以被用户禁用、直接绕过,存在严重安全漏洞 。 正确方案:前端友好提示 + 后端 Validate 强制校验双层防护。
FastAdmin 内置原生支持同名验证器自动加载机制,只需两行控制器属性配置,新增 / 编辑自动触发add/edit验证场景。 本文基于标准 CRUD 生成的控制器、视图完整演示,以商品评价评分(1~5 分)案例实战。
环境说明
- FastAdmin V1.3.x / ThinkPHP5.1
- CRUD 自动生成控制器,原生add/edit事务代码不做大规模修改
- 表单默认格式 name="row字段名"
一、整体架构说明
两层验证分工:
- 前端视图(Nice Validator):页面实时提示,优化用户体验,非强制
- 后端 Validate 验证器:强制校验,不可绕过,核心安全防线
FastAdmin 自动验证机制逻辑:
php
$name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
// $modelSceneValidate=true 自动拼接 .add / .edit 场景
$validate = $name . '.edit';
要求:模型类名 和 Validate 验证器类名完全一致,命名空间保持统一
二、步骤 1:创建 Validate 验证器
路径:application/admin/validate/GoodsEvaluation.php
模型:app\admin\model\GoodsEvaluation 验证器:app\admin\validate\GoodsEvaluation
php
<?php
namespace app\admin\validate;
use think\Validate;
class GoodsEvaluation extends Validate
{
/**
* 验证规则
*/
protected $rule = [
'num' => 'require|number|between:1,5',
'desc' => 'require|max:255',
];
/**
* 自定义错误提示
*/
protected $message = [
'num.require' => '请输入评价分数',
'num.number' => '评价分数必须是数字',
'num.between' => '评价分数必须在1到5之间',
'desc.require' => '请输入评价内容',
'desc.max' => '评价内容最多255个字符',
];
/**
* 验证场景
* add:新增页面;edit:编辑页面
*/
protected $scene = [
'add' => ['num','desc'],
'edit' => ['num','desc'],
];
/**
* 自定义字段中文名称(可选,错误提示更友好)
*/
public function __construct(array $rules = [], $message = [], $field = [])
{
$this->field = [
'num' => __('Num'),
'desc' => __('Desc')
];
parent::__construct($rules, $message, $field);
}
}
⚠️ 避坑重点 规则分隔符必须是英文冒号between:1,5,禁止写成 between1,5,语法错误会导致验证完全失效!
三、步骤 2:控制器开启自动验证
打开控制器 application/admin/controller/GoodsEvaluation.php 在控制器类内部添加两个核心属性:
php
protected $modelValidate = true; // 开启模型自动验证
protected $modelSceneValidate = true; // 开启场景验证(自动识别add/edit)
完整控制器示例片段:
php
class GoodsEvaluation extends Backend
{
protected $model = null;
// 开启自动验证与场景验证【核心配置】
protected $modelValidate = true;
protected $modelSceneValidate = true;
public function _initialize()
{
parent::_initialize();
$this->model = new \app\admin\model\GoodsEvaluation();
}
}
工作原理
开启两个参数后:
- 访问 add 提交:自动加载 scene('add')
- 访问 edit 提交:自动加载 scene('edit')
- 验证失败抛出ValidateException,被 catch 捕获,弹窗输出自定义错误文本
不需要手动new Validate(),原生事务代码完全兼容,无需大幅改造。
四、步骤 3:视图页面添加前端辅助校验(add.html/edit.html)
前端data-rule仅作为体验优化,不能替代后端验证。 很多开发者反馈range(1-5)失效,推荐使用 min + max 写法:
html
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Num')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-num" data-rule="required;integer;range(1~5)" data-msg="请填写1-5的整数分值" class="form-control" name="row[num]" type="number" min="1" max="5" value="{$row.num|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Desc')}:</label>
<div class="col-xs-12 col-sm-8">
<textarea id="c-desc" data-rule="required" class="form-control " rows="5" max="255" name="row[desc]" cols="50">{$row.desc|htmlentities}</textarea>
</div>
</div>
五、常见踩坑清单(高频问题)
坑 1:验证器类名、命名空间和模型不一致
模型:app\admin\model\GoodsEvaluation 验证器必须:app\admin\validate\GoodsEvaluation,大小写必须完全匹配。
坑 2:忘记开启$modelSceneValidate = true
只开modelValidate不会自动读取scene'edit',会执行全部规则,引发意料之外的校验。
坑 3:规则写成between1,5
ThinkPHP 验证规则分隔符为冒号 ✅between:1,5
坑 4:前端 data-rule 正常,关闭 JS 后非法数据可以提交
✅解决方案:永远依靠后端 Validate 作为最终防线,前端仅做体验优化。
坑 5:修改代码后验证不生效
清除项目runtime/cache缓存,浏览器 Ctrl+F5 强制刷新页面。
坑 6:字段名称不匹配
表单提交 rownum,后端$this->request->post("row/a")解析后数组键名就是num,验证器规则字段保持一致即可。
六、拓展方案:不使用自动加载,手动调用验证器
部分场景不想开启全局自动验证,可以在控制器手动调用:
php
use app\admin\validate\GoodsEvaluation;
$params = $this->request->post("row/a");
$validate = new GoodsEvaluation();
if(!$validate->scene('edit')->check($params)){
$this->error($validate->getError());
}
七、总结最佳实践
- 强制后端 Validate 校验,不能省略,杜绝前端绕过风险;
- 控制器开启 modelValidate + modelSceneValidate,复用原生 CRUD 事务代码;
- 验证器区分add/edit场景,按需定义规则;
- 视图增加data-rule前端实时提示,提升操作体验;
- 严格注意命名空间、类名、验证规则语法,避开常见 bug。