表单验证
简介
Laravel 提供了几种不同的方法来验证传入应用程序的数据。最常见的做法是在所有传入的 HTTP 请求中使用 validate
方法。同时,我们还将讨论其他验证方法。
Laravel 包含了各种方便的验证规则,你可以将它们应用于数据,甚至可以验证给定数据库表中的值是否唯一。我们将详细介绍每个验证规则,以便你熟悉 Laravel 的所有验证功能。
快速开始
为了了解 Laravel 强大的验证功能,我们来看一个表单验证并将错误消息展示给用户的完整示例。通过阅读概述,这将会对你如何使用 Laravel 验证传入的请求数据有一个很好的理解:
定义路由
首先,假设我们在 routes/web.php
路由文件 中定义了下面这些路由:
use App\Http\Controllers\PostController;
Route::get('/post/create', [PostController::class, 'create']);
Route::post('/post', [PostController::class, 'store']);
GET
路由会显示一个供用户创建新博客文章的表单,而 POST
路由会将新的博客文章存储到数据库中。
创建控制器
接下来,让我们一起来看看处理这些路由的简单控制器。我们暂时留空了 store 方法:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
class PostController extends Controller
{
/**
* 博客的表单视图
*/
public function create(): View
{
return view('post.create');
}
/**
* 存储博客的 Action
*/
public function store(Request $request): RedirectResponse
{
// 验证并且执行存储逻辑
$post = /** ... */
return to_route('post.show', ['post' => $post->id]);
}
}
编写验证逻辑
现在我们开始在 store
方法中编写用来验证新的博客文章的逻辑代码。为此,我们将使用 Illuminate\Http\Request
类提供的 validate
方法。如果验证通过,你的代码会继续正常运行。如果验证失败,则会抛出 Illuminate\Validation\ValidationException
异常,并自动将对应的错误响应返回给用户。
如果在传统 HTTP 请求期间验证失败,则会生成对先前 URL 的重定向响应。如果传入的请求是 XHR,将将返回包含验证错误信息的 JSON 响应。
为了深入理解 validate
方法,让我们接着回到 store
方法中:
/**
* 存储一篇新的博客文章。
*/
public function store(Request $request): RedirectResponse
{
$validated = $request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
// 博客文章验证通过...
return redirect('/posts');
}
如你所见,验证规则被传递到 validate
方法中。不用担心——所有可用的验证规则均已 存档。 另外再提醒一次,如果验证失败,会自动生成一个对应的响应。如果验证通过,那我们的控制器会继续正常运行。
另外,验证规则可以使用数组,而不是单个 |
分隔的字符串:
$validatedData = $request->validate([
'title' => ['required', 'unique:posts', 'max:255'],
'body' => ['required'],
]);
此外,你可以使用 validateWithBag
方法来验证请求,并将所有错误信息储存在一个 命名错误信息包:
$validatedData = $request->validateWithBag('post', [
'title' => ['required', 'unique:posts', 'max:255'],
'body' => ['required'],
]);
在首次验证失败时停止运行
有时候我们希望某个字段在第一次验证失败后就停止运行验证规则,只需要将 bail
添加到规则中:
$request->validate([
'title' => 'bail|required|unique:posts|max:255',
'body' => 'required',
]);
在这个例子中,如果 title
字段没有通过 unique
规则,那么不会继续验证 max
规则。规则会按照分配时的顺序来验证。
嵌套字段的说明
如果传入的 HTTP 请求包含「嵌套」参数,你可以在验证规则中使用.
语法来指定这些参数:
$request->validate([
'title' => 'required|unique:posts|max:255',
'author.name' => 'required',
'author.description' => 'required',
]);
另外,如果你的字段名称包含点,则可以通过使用反斜杠将点转义,以防止将其解释为.
语法:
$request->validate([
'title' => 'required|unique:posts|max:255',
'v1\.0' => 'required',
]);
显示验证错误信息
那么,如果传入的请求字段没有通过验证规则呢?如前所述,Laravel 会自动将用户重定向到之前的位置。此外,所有的验证错误和请求输入都会自动存入到闪存 session 中。
Illuminate\View\Middleware\ShareErrorsFromSession
中间件与应用程序的所有视图共享一个$errors
变量,该变量由web
中间件组提供。当应用该中间件时,$errors
变量始终在视图中可用,$errors
变量是 Illuminate\Support\MessageBag
的实例。更多有关使用该对象的信息,查看文档
因此,在实例中,当验证失败时,用户将重定向到控制器create
方法,从而在视图中显示错误消息:
<!-- /resources/views/post/create.blade.php -->
<h1>Create Post</h1>
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<!-- Create Post Form -->
在语言文件中指定自定义消息
Laravel 的内置验证规则每个都对应一个错误消息,位于应用程序的lang/en/validation.php
文件中。在此文件中,你将找到每个验证规则的翻译条目。你可以根据应用程序的 需求随意更改或修改这些消息。
此外,你可以将此文件复制到另一个翻译语言目录中,以翻译应用程序语言的消息。要了解有关 Laravel 本地化的更多信息,请查看完整的本地化文档.
默认,Laravel 应用程序框架不包括lang
目录。如果你想自定义 Laravel 的语言文件,你可以通过lang:publish
Artisan 命令发布它们。
XHR 请求 & 验证
在如下示例中,我们使用传统形式将数据发送到应用程序。但是,许多应用程序从 JavaScript 驱动的前端接收 XHR 请求。在 XHR 请求期间使用validate
方法时,Laravel 将不会生成重定向响应。相反,Laravel生成一个包含所有验证错误的 JSON 响应。该 JSON 响应将以 422 HTTP 状态码发送。
@error
指令
你亦可使用 @error
Blade 指令方便地检查给定的属性是否存在验证错误信息。在@error
指令中,你可以输出$message
变量以显示错误信息:
<!-- /resources/views/post/create.blade.php -->
<label for="title">Post Title</label>
<input id="title"
type="text"
name="title"
class="@error('title') is-invalid @enderror">
@error('title')
<div class="alert alert-danger">{{ $message }}</div>
@enderror
如果你使用命名错误包,你可以将错误包的名称作为第二个参数传递给@error
指令:
<input ... class="@error('title', 'post') is-invalid @enderror">
回填表单
当 Laravel 由于验证错误而生成重定向响应时,框架将自动将所有请求的输入闪存到 session 中。这样做是为了方便你在下一个请求期间访问输入,并重新填充用户尝试提交的表单。
要从先前的请求中检索闪存的输入,请在 Illuminate\Http\Request
的实例上调用old
方法。 old
方法将从 session 中提取先前闪存的输入数据:
$title = $request->old('title');
Laravel 还提供了一个全局性的old
。如果要在 Blade 模板, 中显示旧输入,则使用old
来重新填充表单会更加方便。如果给定字段不存在旧输入,则将返回null
:
<input type="text" name="title" value="{{ old('title') }}">
关于可选字段的注意事项
默认情况下, 在你的 Laravel 应用的全局中间件堆栈App\Http\Kernel
类中包含了TrimStrings
和ConvertEmptyStringsToNull
中间件。因此,如果你不想让null
被验证器标识为非法的话,你需要将「可选」字段标志为nullable
。例如:
$request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
'publish_at' => 'nullable|date',
]);
在此示例中,我们指定 publish_at
字段可以为 null
或有效的日期表示。如果没有将 nullable
修饰符添加到规则定义中,则验证器会将 null
视为无效日期。
验证错误响应格式
当您的应用程序抛出 Illuminate\Validation\ValidationException
异常,并且传入的 HTTP 请求希望返回 JSON 响应时,Laravel 将自动为您格式化错误消息,并返回 422 Unprocessable Entity
HTTP 响应。
下面是验证错误的 JSON 响应格式示例。请注意,嵌套的错误键会被转换为“点”符号格式:
{
"message": "The team name must be a string. (and 4 more errors)",
"errors": {
"team_name": [
"The team name must be a string.",
"The team name must be at least 1 characters."
],
"authorization.role": [
"The selected authorization.role is invalid."
],
"users.0.email": [
"The users.0.email field is required."
],
"users.2.email": [
"The users.2.email must be a valid email address."
]
}
}
表单请求验证
创建表单请求
对于更复杂的验证场景,您可能希望创建一个“表单请求”。表单请求是自定义请求类,封装了自己的验证和授权逻辑。要创建一个表单请求类,您可以使用 make:request
Artisan CLI 命令:
php artisan make:request StorePostRequest
生成的表单请求类将被放置在 app/Http/Requests
目录中。如果此目录不存在,则在运行 make:request
命令时将创建该目录。Laravel 生成的每个表单请求都有两个方法:authorize
和 rules
。
你可能已经猜到了,authorize
方法负责确定当前已认证用户是否可以执行请求所代表的操作,而 rules
方法返回应用于请求数据的验证规则:
/**
* 获取应用于请求的验证规则。
*
* @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
*/
public function rules(): array
{
return [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
];
}
你可以在 rules
方法的签名中指定任何你需要的依赖项类型提示。它们将通过 Laravel 的 服务容器 自动解析。
那么,验证规则是如何被评估的呢?你只需要在控制器方法中对请求进行类型提示。在调用控制器方法之前,传入的表单请求将被验证,这意味着你不需要在控制器中添加任何验证逻辑:
/**
* 存储新博客文章。
*/
public function store(StorePostRequest $request): RedirectResponse
{
// 传入的请求有效...
// 检索已验证的输入数据...
$validated = $request->validated();
// Retrieve a portion of the validated input data...
$validated = $request->safe()->only(['name', 'email']);
$validated = $request->safe()->except(['name', 'email']);
// 存储博客文章...
return redirect('/posts');
}
如果验证失败,将生成重定向响应以将用户发送回其先前的位置。错误也将被闪存到会话中,以便进行显示。如果请求是 XHR 请求,则会向用户返回带有 422 状态代码的 HTTP 响应,其中包含JSON 格式的验证错误表示。
在表单请求后添加钩子
如果您想在表单请求「之后」添加验证钩子,可以使用 withValidator
方法。这个方法接收一个完整的验证构造器,允许你在验证结果返回之前调用任何方法:
use Illuminate\Validation\Validator;
/**
* 配置验证实例。
*/
public function withValidator(Validator $validator): void
{
$validator->after(function (Validator $validator) {
if ($this->somethingElseIsInvalid()) {
$validator->errors()->add('field', 'Something is wrong with this field!');
}
});
}