应使用 FormRequest 类进行验证——它将验证逻辑从控制器中解耦,提升可复用性、可测试性与可读性;控制器内仅硬编码 validate() 适用于极简调试场景。

验证规则写在 FormRequest 还是控制器里?
绝大多数情况下,应该用 FormRequest 类——它把验证逻辑从控制器里抽出来,可复用、可测试、可读性高。控制器里硬写 validate() 只适合极简场景(比如单字段调试)。
容易踩的坑:FormRequest 必须显式注册到路由或控制器方法参数中,否则不会触发;且默认只对 POST、PUT、PATCH、DELETE 生效,GET 请求需手动调用 $request->validate()。
-
php artisan make:request StorePostRequest生成类后,在rules()方法里返回规则数组 - 控制器方法签名要写成
function store(StorePostRequest $request),Laravel 才会自动校验并拦截失败请求 - 如果规则依赖运行时数据(比如“不能和数据库已有邮箱重复”),用
Rule::unique('users', 'email')->ignore($id),别直接拼 SQL 字符串
required_if 和 required_unless 怎么选?
这两个规则语义相反,但实际用错会导致表单“不该校验却报错”或“该拦住却放过”。关键看条件字段是否已存在、是否为空。
常见错误现象:选了 required_if:status,active,但 status 字段根本没提交(前端没传),结果验证直接跳过,没拦住空值。
-
required_if:仅当指定字段存在且等于给定值时,当前字段才必须存在且非空 -
required_unless:当指定字段不存在,或存在但不等于给定值时,当前字段才必须存在且非空 - 更安全的做法是配合
nullable或filled使用,例如'phone' => 'required_if:has_phone,1|nullable|string'
自定义错误消息怎么绑定到具体字段?
直接改 resources/lang/zh/validation.php 全局文件,会影响所有地方;用 messages() 方法局部覆盖,又容易漏掉嵌套字段(比如 items.*.price)。
推荐做法:在 FormRequest 的 messages() 方法里,用点号路径精确匹配字段名。
- 字段名带点号(如
profile.phone)必须写成'profile.phone.required' => '手机号不能为空' - 数组字段通配符(如
items.*.name)对应消息键是'items.*.name.required',不是'items.name.required' - 如果想复用语言包里的占位符(如
:attribute),确保自定义消息里保留它,否则显示为字面量
验证通过后,$request->all() 和 $request->validated() 有啥区别?
$request->validated() 只返回通过规则校验的字段,而 $request->all() 返回全部输入(包括未定义规则或被 except 掉的字段)。线上环境用 all() 可能导致意外数据入库。
性能影响很小,但安全边界清晰:只要用了 FormRequest,就该默认用 validated() 接收数据。
-
validated()自动过滤掉没在rules()里声明的字段,防 mass assignment - 如果需要额外字段(比如前端传的
_token或signature),得显式加进规则,哪怕只是'_token' => 'required|string' - 注意:如果规则里用了
sometimes,该字段没传时不会出现在validated()结果中,别假设它一定存在
最常被忽略的是规则与实际业务逻辑的耦合点——比如“密码确认”规则 confirmed 要求存在 password_confirmation 字段,但如果前端传的是 confirm_password,就得自己写 same:confirm_password,而不是指望 Laravel 自动识别。











