formrequest 是 laravel 最可靠易维护的表单验证方式,必须用于字段超3个含条件规则、store/update共用规则、需权限检查、需输入清洗等场景;authorize() 返回 false 会静默终止请求并返回403;prepareforvalidation() 预处理数据后,$request->all() 和 $request->validated() 均返回清洗值;自定义规则须实例化或用 rule::custom() 包装。

FormRequest 是 Laravel 里最可靠、最易维护的表单验证方式——不是“可以选”,而是“该用就用”。它把规则、权限、预处理、错误消息全收进一个类,控制器只剩业务逻辑。
什么时候必须用 FormRequest 而不是 $request->validate()
别等项目大了再重构。以下任一情况出现,立刻建 FormRequest:
- 表单字段超过 3 个,且含
required_if、required_unless、exclude_if等条件规则 - 同一套规则被
store和update两个方法共用 - 需要在验证前检查权限(比如只有管理员能提交
is_pinned字段) - 要对输入做清洗:如自动
trim、转小写、过滤 HTML、把空字符串转为null
authorize() 返回 false 却没报 403?
这是最常见的静默失败点。Laravel 不会“跳过”授权检查,而是直接终止请求流程——但如果你没留意响应状态码或日志,就会以为“验证没触发”。
- 默认生成的
authorize()方法返回false,不改它,所有请求都会被拦截 - 想放行,必须显式返回
true;要做权限判断,用$this->user()->can('create', Post::class)这类逻辑 - 返回
false后,Laravel 不执行rules(),也不收集错误,直接返回 HTTP 403 - 调试时可在
authorize()里加Log::debug('auth check')确认是否运行到这步
验证通过后怎么拿到预处理过的数据?
很多人以为 $request->all() 就是原始输入,其实只要重写了 prepareForValidation(),它返回的就是清洗后的数据。
-
prepareForValidation()在验证前执行,可修改$data数组,比如:$data['published_at'] = $data['published_at'] ?: null; - 验证通过后,
$request->all()、$request->validated()拿到的都是已处理过的值 - 不要在控制器里再手动
array_filter或trim—— 那说明预处理逻辑没写对位置 - 若需保留原始值作审计,可在
prepareForValidation()中存到临时属性:$this->rawInput = $data;,然后在控制器里读取
自定义规则在 FormRequest 里不生效?
FormRequest 的 rules() 数组只认两种东西:字符串规则(如 'email')和实现了 Rule 接口的对象实例(如 new Uppercase)。
- 用
php artisan make:rule Uppercase生成规则类后,必须用new Uppercase实例化,不能写成字符串'Uppercase' - 闭包式规则(如
function ($attribute, $value, $fail) { ... })不能直接塞进 rules 数组,得包装成Rule::custom(...) - 全局注册的规则(用
Validator::extend())可以直接用字符串名,比如'phone' => 'required|chinese_mobile',但前提是已在AppServiceProvider@boot()中注册 - 规则名大小写敏感:
chinese_mobile和ChineseMobile是两个不同规则
FormRequest 的真正复杂点不在写法,而在“边界意识”:它不处理前端渲染、不决定重定向路径、不接管数据库保存——它只做一件事:确保流入控制器的数据干净、合法、符合上下文。漏掉 authorize()、错用规则语法、或在控制器里重复清洗,都是因为模糊了这个边界。











