Laravel表单验证通过validate()或FormRequest实现,需注意CSRF、规则键名匹配、请求类型响应差异、条件规则优先级、FormRequest封装优势及错误消息精准显示方式。

在 Laravel 中,表单验证不是靠写一堆 if 判断实现的,而是通过 validate() 方法或 FormRequest 类统一处理——用对规则、写对位置、注意错误返回方式,才能让验证真正起效。
直接在控制器中使用 validate() 方法
这是最常用也最容易上手的方式,适合简单场景。Laravel 会自动拦截非法请求并重定向回表单页(带错误信息),前提是请求是 POST、PUT 或 PATCH,且使用了 Blade 的 @error 或 $errors 显示错误。
常见错误现象:提交后没报错、页面空白、或报 419 Page Expired —— 多半是 CSRF Token 漏了或验证规则写错导致跳转失败。
- 必须在表单中包含
@csrf指令,否则validate()会因 CSRF 验证失败而抛出异常 - 规则数组里键名要和请求字段名完全一致,比如表单字段是
email,规则就得写'email' => 'required|email' - 如果请求是 API 类型(如
application/json),validate()会直接返回 JSON 错误响应,而不是重定向
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
'password' => 'required|min:8|confirmed',
]);
User::create($validated);
}
required_if 和 required_unless 这类条件规则怎么写才不踩坑
这类规则常被误认为“只要条件满足就校验”,其实它们只控制“是否触发该字段的验证逻辑”,不改变其他规则的行为。例如 required_if:status,active 表示:当 status 字段值为 active 时,当前字段才必须存在且非空;但如果它存在,仍会继续执行后续规则(如 email)。
容易踩的坑:把多个条件规则堆在一起却没理清执行顺序,比如 'phone' => 'required_if:has_phone,1|nullable|regex:/^1[3-9]\d{9}$/',这里 nullable 会让空字符串通过,但 required_if 已经失效——因为 nullable 优先级更高,会提前放行。
- 条件规则应尽量放在前面,避免被
nullable或sometimes干扰 - 多个条件可组合使用,如
required_if:role,admin|required_if:role,editor,等价于“当 role 是 admin 或 editor 时必填” - 注意字段类型:如果
status是整型数据库字段,但表单传的是字符串"1",required_if:status,1依然能匹配;但若传的是"true",就得写成required_if:status,true
自定义验证规则用 Validator::make() 还是 FormRequest?
当验证逻辑变复杂(比如要查数据库、调外部接口、或复用到多个控制器),硬编码在控制器里会很快失控。FormRequest 是更清晰的选择:它把验证规则、授权逻辑、甚至预处理都封装在一个类里,还能用 php artisan make:request 快速生成。
性能影响很小,但要注意:每个 FormRequest 类都会被容器解析一次,如果规则里有耗时操作(如多次 DB 查询),建议提取到 withValidator() 或单独服务中。
-
authorize()方法返回false会直接返回 403,适合权限前置判断 -
rules()返回规则数组,支持动态构建,比如根据用户角色返回不同规则 - 想在验证前修改数据(如 trim 空格、转小写),重写
prepareForValidation()方法比在控制器里手动处理更可靠
class StorePostRequest extends FormRequest
{
public function authorize()
{
return $this->user()->can('create', Post::class);
}
public function rules()
{
return [
'title' => 'required|string|min:5',
'slug' => 'required|unique:posts,slug,NULL,id,user_id,' . $this->user()->id,
];
}
protected function prepareForValidation()
{
$this->merge([
'slug' => Str::slug($this->title),
]);
}
}
验证失败后错误消息怎么精准显示到对应字段
Laravel 默认把所有错误存进 $errors(ViewErrorBag 实例),Blade 中用 @error('field') 提取是最稳妥的方式。别直接读 $errors->first('field') 或 $errors->get('field'),除非你明确知道当前上下文(比如 API 响应)。
容易被忽略的点:AJAX 提交时,Laravel 默认仍会重定向,导致前端收不到 JSON 错误。必须确保请求头包含 X-Requested-With: XMLHttpRequest,或者显式指定 Accept: application/json,Laravel 才会返回 JSON 格式的错误(含 422 Unprocessable Entity 状态码)。
- Blade 中显示单个字段错误:
@error('email') {{ $message }} @enderror - API 场景下,前端需检查响应状态码是否为 422,并解析
errors字段对象 - 自定义错误消息不要全写在
messages()里,优先用语言文件resources/lang/zh/validation.php统一管理
验证规则本身没有魔法,关键是理解每条规则的触发时机、字段值的原始类型、以及 Laravel 在不同请求类型下的响应策略。最常出问题的不是规则写错,而是没意识到 validate() 会中断流程、或忽略了请求头对响应格式的决定性影响。










