Request 类与表单验证
High Contrast
Dark Mode
Light Mode
Sepia
Forest
1 min read194 words

Request 类与表单验证

把验证规则写在控制器方法里,控制器会越来越臃肿。Laravel 的 FormRequest 类让你把验证逻辑单独封装——控制器方法接收到请求时,数据已经验证通过,否则自动返回 422 错误。


创建 FormRequest

php artisan make:request StoreTaskRequest
php artisan make:request UpdateTaskRequest
<?php
// app/Http/Requests/StoreTaskRequest.php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreTaskRequest extends FormRequest
{
// authorize():当前用户是否有权限提交此请求
public function authorize(): bool
{
// true = 允许所有认证用户
// 复杂场景可以用 Gate / Policy 检查
return auth()->check();
}
// rules():验证规则
public function rules(): array
{
return [
'title'       => ['required', 'string', 'max:200'],
'description' => ['nullable', 'string', 'max:5000'],
'status'      => ['required', 'in:pending,in_progress,done'],
'priority'    => ['required', 'integer', 'between:1,5'],
'due_date'    => ['nullable', 'date', 'after:today'],
'tags'        => ['nullable', 'array', 'max:10'],
'tags.*'      => ['string', 'max:50'],  // 数组元素规则
'team_id'     => [
'nullable',
'integer',
// 自定义验证:只能选择自己所属的团队
Rule::exists('team_user', 'team_id')->where('user_id', auth()->id()),
],
];
}
// messages():自定义错误信息(可选)
public function messages(): array
{
return [
'title.required' => '任务标题不能为空',
'title.max'      => '任务标题不能超过 200 个字符',
'due_date.after' => '截止日期必须是未来的日期',
];
}
// attributes():字段名称别名(用于错误信息)
public function attributes(): array
{
return [
'due_date' => '截止日期',
'team_id'  => '所属团队',
];
}
// 验证通过后的数据预处理
protected function passedValidation(): void
{
// 可以在这里做数据转换
$this->merge([
'user_id' => auth()->id(),
]);
}
}

在控制器中使用

<?php
// app/Http/Controllers/TaskController.php
namespace App\Http\Controllers;
use App\Http\Requests\StoreTaskRequest;
use App\Http\Requests\UpdateTaskRequest;
use App\Repositories\TaskRepositoryInterface;
class TaskController extends Controller
{
public function __construct(
private TaskRepositoryInterface $tasks
) {}
// 方法签名中注入 FormRequest,会自动验证
// 验证失败时自动返回 422(API)或重定向回上一页(Web)
public function store(StoreTaskRequest $request)
{
// 到这里数据已经验证通过
$task = $this->tasks->create($request->validated());
// validated() 只返回已通过规则的字段,防止批量赋值攻击
return new TaskResource($task);
}
public function update(UpdateTaskRequest $request, Task $task)
{
// 只更新提交的字段(PATCH 语义)
$task = $this->tasks->update($task->id, $request->validated());
return new TaskResource($task);
}
}

常用验证规则速查

// 字符串
'title'    => ['required', 'string', 'min:2', 'max:200'],
'slug'     => ['required', 'alpha_dash'],       // 字母数字横线下划线
'email'    => ['required', 'email:rfc,dns'],    // 验证格式 + DNS
// 数值
'price'    => ['required', 'numeric', 'min:0', 'max:999999'],
'count'    => ['required', 'integer', 'between:1,100'],
// 日期
'due_date' => ['nullable', 'date_format:Y-m-d', 'after:today'],
'starts_at'=> ['required', 'date'],
'ends_at'  => ['required', 'date', 'after:starts_at'],
// 唯一性(数据库检查)
use Illuminate\Validation\Rule;
'email'    => ['required', 'email', Rule::unique('users')->ignore($this->user)],
'slug'     => ['required', Rule::unique('tasks')->where('user_id', auth()->id())],
// 存在性检查
'category_id' => ['required', 'exists:categories,id'],
'user_ids'    => ['required', 'array'],
'user_ids.*'  => ['integer', 'exists:users,id'],
// 条件验证
'phone'    => ['required_if:contact_method,phone', 'nullable', 'string'],
'url'      => ['required_unless:type,offline', 'nullable', 'url'],
// 文件上传
'avatar'   => ['nullable', 'image', 'mimes:jpg,jpeg,png,webp', 'max:2048'],  // 最大 2MB
'document' => ['required', 'file', 'mimes:pdf,docx', 'max:10240'],           // 最大 10MB

自定义验证规则

php artisan make:rule NoForbiddenWords
<?php
// app/Rules/NoForbiddenWords.php
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
class NoForbiddenWords implements ValidationRule
{
private array $forbidden = ['spam', 'ads', 'promo'];
public function validate(string $attribute, mixed $value, Closure $fail): void
{
foreach ($this->forbidden as $word) {
if (str_contains(strtolower($value), $word)) {
$fail("$attribute 包含禁用词汇:{$word}");
return;
}
}
}
}
// 使用:
'title' => ['required', 'string', new NoForbiddenWords()],

验证错误响应格式

Laravel 对 API 请求(Accept: application/json)返回标准 422 响应:

{
"message": "The given data was invalid.",
"errors": {
"title": [
"任务标题不能为空"
],
"due_date": [
"截止日期必须是未来的日期"
]
}
}

下一节响应类型与资源路由——控制器除了返回 JSON 外,还需要返回重定向、文件下载、Stream 响应。资源路由的 7 个标准方法是 RESTful 设计的基础。