验证规则应写在独立的 Form Request 类中,如 StorePostRequest;自定义规则优先用 Rule 对象,复杂逻辑用自定义 Rule 类;错误消息通过 messages() 或 Rule::message() 精准覆盖;空值处理需在 prepareForValidation() 预处理。

验证规则写在哪?别往 Controller 里硬塞
绝大多数人第一次写 Laravel 表单验证,会直接在 store 或 update 方法里调用 validate() —— 这能跑通,但很快会失控。规则分散、复用难、测试麻烦,而且一旦要加自定义错误消息或条件逻辑,代码就变面条。
正确做法是用独立的 Form Request 类:php artisan make:request StorePostRequest。它自动继承 FormRequest,自带 authorize() 和 rules() 方法。
-
rules()返回数组,和控制器里写的规则格式完全一样,比如['title' => 'required|string|max:255'] - 验证失败时自动重定向并携带错误,不用手动
return redirect()->back()->withErrors(...) - 可以放心把权限判断(如是否允许提交)写进
authorize(),和验证逻辑解耦
自定义验证规则:用 Rule 对象还是闭包?
内置规则不够用时,比如“不能和数据库里已存在的邮箱重复但排除当前用户”,用闭包写法最直接,但只适合单次使用;如果多个地方都要校验“用户名不能包含敏感词”,就得注册全局规则。
推荐优先用 Rule 对象组合:它可读性强、支持参数化,且天然兼容 required_if 等依赖型规则。
- 例如禁止邮箱重复但忽略自己:
Rule::unique('users', 'email')->ignore($user->id) - 闭包适合临时逻辑,比如检查 JSON 字段结构:
function ($attribute, $value, $fail) { ... },但别在里面做耗时操作(如查库),否则影响响应速度 - 真要复用复杂逻辑,用
php artisan make:rule UppercaseFirstLetter创建类,然后在passes()里写判断
错误消息怎么改得准?别只动 resources/lang/en/validation.php
改语言包文件确实能统一替换“The :attribute field is required”,但实际项目里更常见的是:某个字段需要更具体的提示,比如“密码至少8位且含大小写字母”,而其他地方仍用默认提示。
- 在 Form Request 的
messages()方法里返回字段级覆盖,键名是"field.rule"格式,例如'password.confirmed' => '两次输入的密码不一致,请重新填写' - 如果用了自定义 Rule 类,在类里重写
message()方法,返回字符串即可,比全局语言包更精准 - 注意:中文环境下,
:attribute占位符默认显示英文字段名(如email),想显示“邮箱”,得在attributes()方法里配映射:['email' => '邮箱']
前端传空数组或 null,后端验证为啥没拦住?
Laravel 默认对 null 值跳过大部分验证(除了 required 系列),而前端 JS 提交空数组 [] 或字符串 "null",常被当成“有值”通过了 required,结果进到逻辑里才报错。
- 加
required|array能挡住null和非数组类型,但挡不住空数组[]—— 它确实是数组 - 要拒绝空数组,得补上
min:1或用自定义规则:Rule::requiredIf(fn() => is_array($data['tags']) && count($data['tags']) === 0) - 更稳妥的做法是在 Form Request 的
prepareForValidation()里预处理:把null或空字符串转成null,再统一用nullable控制
验证不是越严越好,而是要和前端行为对齐。很多“验证失效”的问题,其实是前后端对空值的理解不一致导致的。










