用户输入的数据是不可信的,因此验证是 Web 应用安全性的重要防线。Laravel 提供了多种验证机制,让你能够轻松定义规则、自定义错误消息,并将验证逻辑与控制器分离。 本章将全面介绍 Laravel 的数据验证功能,以及如何在前端优雅地展示错误提示。
在控制器中,使用 validate 方法对请求数据进行验证。如果验证失败,会自动重定向回上一页并携带错误信息。
use Illuminate\Http\Request;
public function store(Request $request)
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'content' => 'required',
'email' => 'required|email|unique:users,email',
'age' => 'nullable|integer|min:18',
]);
// 验证通过,$validated 包含已验证的数据
// 继续业务逻辑...
}
>
也可以使用数组形式定义规则,更易读:
$request->validate([
'title' => ['required', 'string', 'max:255'],
'content' => ['required'],
'email' => ['required', 'email', 'unique:users,email'],
]);
>
old()。
required — 字段必须存在且不为空。required_if:another,value — 当另一个字段等于某值时该字段必填。string — 字段必须是字符串类型。integer — 必须是整数。numeric — 必须是数值。email — 必须是有效的邮箱格式。url — 必须是有效的 URL。date — 必须是有效的日期格式。min:value — 数值最小值,或字符串最小长度。max:value — 数值最大值,或字符串最大长度。between:min,max — 数值或长度介于 min 和 max 之间。unique:table,column — 在指定表中必须唯一(排除当前记录时可用 unique:users,email,{$id})。exists:table,column — 必须存在于指定表的字段中。confirmed — 必须与 _confirmation 字段匹配(如 password 与 password_confirmation)。image — 上传文件必须是图片(jpg、png等)。mimes:jpg,png — 限制文件 MIME 类型。你可以为每个规则自定义错误消息,支持字段占位符 :attribute、:value 等:
$request->validate([
'title' => 'required|max:255',
'email' => 'required|email|unique:users',
], [
'title.required' => '标题不能为空',
'title.max' => '标题不能超过 255 个字符',
'email.required' => '邮箱地址是必填项',
'email.email' => '请输入有效的邮箱地址',
'email.unique' => '该邮箱已被注册',
]);
>
还可以使用 attributes 数组自定义字段名称:
$request->validate([
'user.email' => 'required|email',
], [
'user.email.required' => '邮箱不能为空',
], [
'user.email' => '用户邮箱',
]);
>
当验证逻辑变得复杂时,建议使用表单请求类将验证规则独立出来,保持控制器简洁。
创建表单请求类:
php artisan make:request StorePostRequest
生成的文件位于 app/Http/Requests,在其中定义验证规则和错误消息:
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StorePostRequest extends FormRequest
{
public function authorize(): bool
{
return true; // 可在此进行权限验证
}
public function rules(): array
{
return [
'title' => 'required|string|max:255',
'content' => 'required',
'category_id' => 'required|exists:categories,id',
];
}
public function messages(): array
{
return [
'title.required' => '标题不能为空',
'content.required' => '内容不能为空',
'category_id.exists' => '请选择有效的分类',
];
}
}
在控制器中类型提示该请求,Laravel 会自动验证:
public function store(StorePostRequest $request)
{
// 验证已通过,直接使用 $request 获取数据
$post = Post::create($request->validated());
return redirect()->route('posts.index');
}
authorize() 进行权限检查)。
在视图中,$errors 变量自动可用,它是一个 MessageBag 实例,包含所有验证错误信息。
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<input type="text" name="title" value="{{ old('title') }}">
@error('title')
<div class="error-message">{{ $message }}</div>
@enderror
>
<input type="email" name="email" class="form-control @error('email') is-invalid @enderror" value="{{ old('email') }}">
@error('email')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
>
如果请求期望 JSON 响应(如 AJAX 请求),验证失败时会返回 JSON 格式的错误信息,状态码为 422。
前端示例(使用 Fetch API):
fetch('/api/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify(data)
})
.then(response => {
if (!response.ok) {
return response.json().then(err => { throw err; });
}
return response.json();
})
.catch(err => {
if (err.errors) {
// 处理验证错误
Object.keys(err.errors).forEach(field => {
console.log(`${field}: ${err.errors[field].join(', ')}`);
});
}
});
>
1. 表单请求类 StorePostRequest.php
public function rules(): array
{
return [
'title' => 'required|string|max:255|unique:posts,title',
'content' => 'required|string|min:10',
'category_id' => 'required|exists:categories,id',
'tags' => 'array',
'tags.*' => 'exists:tags,id',
'image' => 'nullable|image|max:2048',
];
}
public function messages(): array
{
return [
'title.unique' => '标题已存在,请更换',
'content.min' => '内容至少需要 10 个字符',
'category_id.exists' => '分类不存在',
'tags.*.exists' => '所选标签无效',
'image.image' => '请上传有效的图片文件',
'image.max' => '图片不能超过 2MB',
];
}
2. 控制器方法
public function store(StorePostRequest $request)
{
$data = $request->validated();
if ($request->hasFile('image')) {
$data['image'] = $request->file('image')->store('posts', 'public');
}
$post = Post::create($data);
$post->tags()->sync($data['tags'] ?? []);
return redirect()->route('posts.show', $post)->with('success', '文章发布成功!');
}
3. 视图表单(简化)
<form method="POST" action="{{ route('posts.store') }}" enctype="multipart/form-data">
@csrf
<div class="mb-3">
<label>标题</label>
<input type="text" name="title" class="form-control @error('title') is-invalid @enderror" value="{{ old('title') }}">
@error('title')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label>内容</label>
<textarea name="content" class="form-control @error('content') is-invalid @enderror">{{ old('content') }}</textarea>
@error('content')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label>分类</label>
<select name="category_id" class="form-select @error('category_id') is-invalid @enderror">
<option value="">请选择</option>
@foreach ($categories as $category)
<option value="{{ $category->id }}" {{ old('category_id') == $category->id ? 'selected' : '' }}>
{{ $category->name }}
</option>
@endforeach
</select>
@error('category_id')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label>图片</label>
<input type="file" name="image" class="form-control @error('image') is-invalid @enderror">
@error('image')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<button type="submit" class="btn btn-primary">发布文章</button>
</form>
validate 时无法直接指定重定向地址,但可以在表单请求类中重写 $redirect 属性或 getRedirectUrl() 方法。或者捕获验证异常手动处理。
'items.*.name' => 'required|string',验证 items 数组中每个子项的 name 字段。
sometimes 规则:$request->validate(['field' => 'sometimes|required|...']); 当字段存在时才验证。
数据验证是保证应用质量的关键环节。Laravel 提供了灵活的验证方式,从简单的控制器验证到高级的表单请求类,配合强大的规则库和自定义错误消息,可以应对各种复杂场景。 配合视图中便捷的错误显示,能有效提升用户体验。下一章我们将学习会话管理,掌握用户状态维护的技巧。