Laravel Form Request表单验证

2026-09-03 46

用户注册、文章发布、订单提交和文件上传等功能,都需要在服务端检查请求参数。如果把所有验证规则都写在控制器中,代码很快就会变得臃肿。Laravel 提供的 Form Request 可以把参数验证和授权逻辑放进独立请求类,让控制器只负责处理已经通过验证的数据。

一、什么是Laravel Form Request

Form Request 是 Laravel 中专门用于封装请求验证和授权逻辑的自定义请求类。它继承自 Laravel 的基础请求对象,可以定义验证规则、错误信息、字段名称和请求授权条件。

与直接在控制器中调用 $request->validate() 相比,Form Request 更适合以下场景:

  • 表单字段较多;
  • 验证规则需要重复使用;
  • 控制器需要保持简洁;
  • 接口需要统一返回验证错误;
  • 请求提交前需要先判断操作权限。

如果只是验证一两个简单字段,直接使用 $request->validate() 也没有问题。随着业务复杂度提高,再把验证逻辑拆到 Form Request 中会更容易维护。

二、创建Form Request请求类

在 Laravel 项目根目录执行以下命令:

php artisan make:request StorePostRequest

执行后,Laravel 会在 app/Http/Requests 目录下创建 StorePostRequest.php 文件。

基础结构如下:

<?php

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:200'],
            'content' => ['required', 'string'],
            'status' => ['required', 'integer', 'in:0,1'],
        ];
    }
}

authorize() 用于判断当前用户是否有权执行这个请求,rules() 用于定义字段验证规则。

如果 authorize() 返回 false,Laravel 会拒绝请求;如果暂时不需要权限判断,应明确返回 true

三、在控制器中使用Form Request

创建请求类后,在控制器方法中注入 StorePostRequest

<?php

namespace App\Http\Controllers;

use App\Http\Requests\StorePostRequest;
use App\Models\Post;

class PostController extends Controller
{
    public function store(StorePostRequest $request)
    {
        $post = Post::create($request->validated());

        return redirect()
            ->route('posts.show', $post)
            ->with('success', '文章创建成功');
    }
}

Laravel 会在进入 store() 方法前自动执行验证。验证失败时,传统表单请求通常会返回上一页并携带错误信息;对于预期返回 JSON 的请求,Laravel 会返回包含验证错误的 JSON 响应。

验证通过后,可以通过以下方法取得有效数据:

$data = $request->validated();

也可以通过 safe() 获取经过验证的数据容器:

$data = $request->safe()->only([
    'title',
    'content',
    'status',
]);

这样可以避免把未经验证的额外字段直接写入数据库。

四、常用Laravel验证规则

Laravel 提供了较完整的验证规则,日常项目中常用的包括:

public function rules(): array
{
    return [
        'name' => ['required', 'string', 'max:100'],
        'email' => ['required', 'email', 'unique:users,email'],
        'password' => ['required', 'string', 'min:8', 'confirmed'],
        'age' => ['nullable', 'integer', 'min:18'],
        'website' => ['nullable', 'url'],
        'status' => ['required', 'in:draft,published'],
        'published_at' => ['nullable', 'date'],
        'category_id' => ['required', 'exists:categories,id'],
    ];
}

这些规则分别完成必填、字符串长度、邮箱格式、唯一性、密码确认、数字范围、网址格式、指定值范围、日期格式和关联记录存在性检查。

数组写法比使用竖线拼接规则更清晰,尤其适合规则中包含对象或复杂条件的情况。

五、自定义错误提示

默认错误提示可以满足基础使用,但中文项目通常需要根据页面场景自定义文案。

在 Form Request 中增加 messages() 方法:

public function messages(): array
{
    return [
        'title.required' => '请输入文章标题',
        'title.max' => '文章标题不能超过200个字符',
        'content.required' => '请输入文章正文',
        'status.in' => '文章状态参数不正确',
    ];
}

数组键由“字段名.规则名”组成。例如,title.required 表示 title 字段没有通过 required 规则。

还可以通过 attributes() 定义更加友好的字段名称:

public function attributes(): array
{
    return [
        'title' => '文章标题',
        'content' => '文章正文',
        'status' => '文章状态',
    ];
}

定义后,Laravel 在生成错误信息时会使用这些字段名称。

六、表单页面怎么显示验证错误

Blade 模板可以使用 $errors 变量显示错误信息:

@if ($errors->any())
    <div class="alert alert-danger">
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

如果只显示指定字段的错误,可以使用 @error

<input
    type="text"
    name="title"
    value="{{ old('title') }}"
>

@error('title')
    <div class="text-danger">{{ $message }}</div>
@enderror

old('title') 可以恢复用户上一次提交的内容,避免验证失败后重新填写整个表单。

七、编辑数据时处理唯一性验证

创建用户时,可以直接验证邮箱唯一:

'email' => ['required', 'email', 'unique:users,email'],

编辑用户时,需要忽略当前记录,否则原邮箱也会被判断为重复。可以使用 Rule::unique()->ignore()

use Illuminate\Validation\Rule;

public function rules(): array
{
    return [
        'email' => [
            'required',
            'email',
            Rule::unique('users', 'email')->ignore($this->user),
        ],
    ];
}

传给 ignore() 的值应来自可信的模型实例或系统生成的 ID,不能直接使用用户可控的请求参数,避免引入 SQL 注入风险。

八、根据请求条件动态验证

部分字段是否必填,可能取决于另一个字段。例如,文章状态为已发布时,必须填写发布时间:

public function rules(): array
{
    return [
        'status' => ['required', 'in:draft,published'],
        'published_at' => [
            'nullable',
            'date',
            'required_if:status,published',
        ],
    ];
}

如果规则需要更复杂的判断,可以使用 withValidator()after() 添加验证后的检查逻辑。

示例:

use Illuminate\Validation\Validator;

public function after(): array
{
    return [
        function (Validator $validator) {
            if (
                $this->input('status') === 'published'
                && empty($this->input('content'))
            ) {
                $validator->errors()->add(
                    'content',
                    '发布文章前必须填写正文'
                );
            }
        },
    ];
}

这类写法适合跨字段判断,但简单条件优先使用 Laravel 已有验证规则,代码会更容易理解。

九、验证前整理请求数据

如果前端提交的数据需要先清理,可以在 prepareForValidation() 中处理:

protected function prepareForValidation(): void
{
    $this->merge([
        'title' => trim((string) $this->title),
        'status' => $this->status ?: 'draft',
    ]);
}

该方法会在正式验证前执行,适合清理空格、统一格式或补充默认值。

不要在这里处理复杂业务逻辑,也不要把无法信任的输入直接转换成拥有更高权限的字段。

十、使用authorize进行请求授权

Form Request 不只负责参数验证,也可以判断当前用户是否允许执行操作。

例如,只允许文章作者更新文章:

public function authorize(): bool
{
    return $this->user()?->can('update', $this->post) ?? false;
}

这里通常配合 Laravel Policy 使用。授权失败时,Laravel 会返回禁止访问响应,控制器不会继续执行。

参数验证解决的是“提交的数据是否正确”,授权解决的是“当前用户能否执行操作”。这两个概念不能混在一起。

十一、API验证失败怎么处理

当请求预期返回 JSON 时,Laravel 会返回验证错误信息,常见状态码为 422 Unprocessable Content。前端可以根据 errors 字段,将不同字段的错误显示在表单旁边。

请求示例:

{
  "title": "",
  "content": "测试内容",
  "status": "unknown"
}

验证失败后,响应中会包含具体字段错误。前端不应该只显示“提交失败”,而应把标题不能为空、状态参数不正确等信息准确反馈给用户。

如项目需要统一接口格式,可以在异常处理层集中转换验证异常,但要保留字段级错误,方便前端定位问题。

  • 广告合作

  • QQ群号:4114653

温馨提示:
1、本网站发布的内容(图片、视频和文字)以原创、转载和分享网络内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。邮箱:2942802716#qq.com(#改为@)。 2、本站原创内容未经允许不得转裁,转载请注明出处“站长百科”和原文地址。