如何使用Laravel验证器校验API请求参数?
- 内容介绍
- 文章标签
- 相关推荐
本文共计878个文字,预计阅读时间需要4分钟。
很多人把验证逻辑写进+FormRequest+,但改着改着把+rules()+写成了+return [];+,或者条件不匹配导致返回空数组,结果请求请求完成不校验就进去了控制器——导致+422+。都不会触发异常。
- 务必确保
rules()至少返回一个非空数组,哪怕只是['id' => 'required|integer'] - 如果想动态开关校验,用
withValidator()做拦截,而不是清空rules() - 测试时手动发个缺失必填字段的请求,看是否真返回
422和错误信息,别只看代码有没有写validate
Laravel 10+ 的 validated() 和 safe()->all() 行为不同
validated() 返回的是经过规则过滤后的数据(比如你写了 'email' => 'required|email',它只返回 email 字段),而 safe()->all() 是把所有通过校验的字段全吐出来,包括没在 rules() 里声明的字段(只要没被 only() 限制)。
- 如果你依赖
request->validated()['xxx']取值,但实际传了xxx却没写进rules(),这里会报Undefined array key - 想严格按规则取值,用
validated();想兼容额外参数(比如前端埋点字段),用safe()->all()并自己做字段白名单判断 -
safe()在 Laravel 9.28+ 才稳定,低版本会抛Method does not exist错误
自定义错误消息写在 messages() 里,但中文键名要加引号
写 'title.required' => '标题不能为空' 没问题,但写成 title.required => '标题不能为空'(不加引号)会导致 PHP 解析失败,报 ParseError: syntax error。
- 所有含点号、中划线、中文的数组键名,必须用单引号或双引号包裹
- 如果用了
trans()做多语言,确保对应语言文件存在,否则显示空字符串,不是报错 - 批量替换错误消息时,容易漏掉嵌套规则里的键名,比如
'tags.*.required'这种通配写法也要加引号
API 验证失败默认返回 HTML 页面,不是 JSON
开箱即用的 FormRequest 在 API 路由里失败时,如果没显式指定响应格式,Laravel 仍可能返回重定向或 HTML 错误页,尤其当你混用了 web 和 api 中间件组。
- 确认路由在
routes/api.php里,并且没被web中间件污染(比如全局绑了web) - 在
App\Exceptions\Handler.php的render()方法里检查是否对ValidationException做了 JSON 包装,没做的话会走默认逻辑 - 最简单兜底:在
FormRequest里加public function response(array $errors) { return response()->json($errors, 422); }
safe() 和 validated() 的边界容易模糊,上线前最好用 Postman 实测几个非法请求。本文共计878个文字,预计阅读时间需要4分钟。
很多人把验证逻辑写进+FormRequest+,但改着改着把+rules()+写成了+return [];+,或者条件不匹配导致返回空数组,结果请求请求完成不校验就进去了控制器——导致+422+。都不会触发异常。
- 务必确保
rules()至少返回一个非空数组,哪怕只是['id' => 'required|integer'] - 如果想动态开关校验,用
withValidator()做拦截,而不是清空rules() - 测试时手动发个缺失必填字段的请求,看是否真返回
422和错误信息,别只看代码有没有写validate
Laravel 10+ 的 validated() 和 safe()->all() 行为不同
validated() 返回的是经过规则过滤后的数据(比如你写了 'email' => 'required|email',它只返回 email 字段),而 safe()->all() 是把所有通过校验的字段全吐出来,包括没在 rules() 里声明的字段(只要没被 only() 限制)。
- 如果你依赖
request->validated()['xxx']取值,但实际传了xxx却没写进rules(),这里会报Undefined array key - 想严格按规则取值,用
validated();想兼容额外参数(比如前端埋点字段),用safe()->all()并自己做字段白名单判断 -
safe()在 Laravel 9.28+ 才稳定,低版本会抛Method does not exist错误
自定义错误消息写在 messages() 里,但中文键名要加引号
写 'title.required' => '标题不能为空' 没问题,但写成 title.required => '标题不能为空'(不加引号)会导致 PHP 解析失败,报 ParseError: syntax error。
- 所有含点号、中划线、中文的数组键名,必须用单引号或双引号包裹
- 如果用了
trans()做多语言,确保对应语言文件存在,否则显示空字符串,不是报错 - 批量替换错误消息时,容易漏掉嵌套规则里的键名,比如
'tags.*.required'这种通配写法也要加引号
API 验证失败默认返回 HTML 页面,不是 JSON
开箱即用的 FormRequest 在 API 路由里失败时,如果没显式指定响应格式,Laravel 仍可能返回重定向或 HTML 错误页,尤其当你混用了 web 和 api 中间件组。
- 确认路由在
routes/api.php里,并且没被web中间件污染(比如全局绑了web) - 在
App\Exceptions\Handler.php的render()方法里检查是否对ValidationException做了 JSON 包装,没做的话会走默认逻辑 - 最简单兜底:在
FormRequest里加public function response(array $errors) { return response()->json($errors, 422); }
safe() 和 validated() 的边界容易模糊,上线前最好用 Postman 实测几个非法请求。
