Laravel 的表单验证机制详解
简介
Laravel 提供了几种不同的方法来验证传入应用程序的数据。默认情况下,Laravel 的控制器基类使用 ValidatesRequests
Trait,它提供了一种方便的方法使用各种强大的验证规则来验证传入的 HTTP 请求。
快速验证
为了解 Laravel 强大的验证功能,我们先来看看一个完整的验证表单并返回错误消息的示例。
定义路由
首先,假设我们在 routes/web.php
文件中定义了以下路由:
Route::get('post/create', 'PostController@create');
Route::post('post', 'PostController@store');
GET
路由用来显示一个供用户创建新的博客文章的表单,POST
路由则是会将新的博客文章保存到数据库。
创建控制器
下一步,我们来看一个处理这些路由的控制器。我们将 store
方法置空:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class PostController extends Controller
{
/**
* 显示创建博客文章的表单。
*
* @return Response
*/
public function create()
{
return view('post.create');
}
/**
* 保存一个新的博客文章。
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
// 验证以及保存博客文章...
}
}
编写验证逻辑
现在我们准备开始在 store
方法中编写逻辑来验证新的博客文章。为此,我们将使用 Illuminate\Http\Request
对象提供的 validate
方法 。如果验证通过,你的代码就可以正常的运行。但是如果验证失败,就会抛出异常,并自动将对应的错误响应返回给用户。在典型的 HTTP 请求的情况下,会生成一个重定向响应,而对于 AJAX 请求则会发送 JSON 响应。
让我们接着回到 store
方法来深入理解 validate
方法:
/**
* 保存一篇新的博客文章。
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
$this->validate($request, [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
// 文章内容是符合规则的,存入数据库
}
如你所见,我们将所需的验证规则传递至 validate
方法中。另外再提醒一次,如果验证失败,会自动生成一个对应的响应。如果验证通过,那我们的控制器将会继续正常运行。
在第一次验证失败后停止
有时,你希望在某个属性第一次验证失败后停止运行验证规则。为了达到这个目的,附加 bail
规则到该属性:
$this->validate($request, [
'title' => 'bail|required|unique:posts|max:255',
'body' => 'required',
]);
在这个例子里,如果 title
字段没有通过 unique
,那么不会检查 max
规则。规则会按照分配的顺序来验证。
关于数组数据的注意事项
如果你的 HTTP 请求包含一个 「嵌套」 参数(即数组),那你可以在验证规则中通过 「点」 语法来指定这些参数。
$this->validate($request, [
'title' => 'required|unique:posts|max:255',
'author.name' => 'required',
'author.description' => 'required',
]);
显示验证错误
如果传入的请求参数未通过给定的验证规则呢?正如前面所提到的,Laravel 会自动把用户重定向到先前的位置。另外,所有的验证错误信息会被自动 闪存至 session。
重申一次,我们不必在 GET
路由中将错误消息显式绑定到视图。因为 Lavarel 会检查在 Session 数据中的错误信息,并自动将其绑定到视图(如果存在)。而其中的变量 $errors
是 Illuminate\Support\MessageBag
的一个实例。要获取关于这个对象的更多信息,请 查阅这个文档。
$errors
变量被由Web中间件组提供的 Illuminate\View\Middleware\ShareErrorsFromSession
中间件绑定到视图。当这个中间件被应用后,在你的视图中就可以获取到 $errors
变量,可以使一直假定 $errors
变量存在并且可以安全地使用。
所以,在我们的例子中,当验证失败的时候,用户将会被重定向到控制器的 create
方法,让我们在视图中显示错误信息:
<!-- /resources/views/post/create.blade.php -->
<h1>创建文章</h1>
@if (count($errors) > 0)
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<!-- 创建文章表单 -->
可选字段上的注意事项
默认情况下,Laravel 在你应用的全局中间件堆栈中包含在 App\Http\Kernel
类中的 TrimStrings
和 ConvertEmptyStringsToNull
中间件。因此,如果你不希望验证程序将 null
值视为无效的,那就将「可选」的请求字段标记为 nullable
。
$this->validate($request, [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
'publish_at' => 'nullable|date',
]);
在这个例子里,我们指定 publish_at
字段可以为 null
或者一个有效的日期格式。如果 nullable
的修饰词没有被添加到规则定义中,验证器会认为 null
是一个无效的日期格式。
AJAX 请求 & 验证
在这个例子中,我们使用传统的表单将数据发送到应用程序。但实际情况中,很多程序都会使用 AJAX 来发送请求。当我们对 AJAX 的请求中使用 validate
方法时,Laravel 并不会生成一个重定向响应,而是会生成一个包含所有验证错误信息的 JSON 响应。这个 JSON 响应会包含一个 HTTP 状态码 422 被发送出去。
表单请求验证
创建表单请求
面对更复杂的验证情境中,你可以创建一个「表单请求」来处理更为复杂的逻辑。表单请求是包含验证逻辑的自定义请求类。可使用 Artisan 命令 make:request
来创建表单请求类:
php artisan make:request StoreBlogPost
新生成的类保存在 app/Http/Requests
目录下。如果这个目录不存在,运行 make:request
命令时它会被创建出来。让我们添加一些验证规则到 rules
方法中:
/**
* 获取 适用于请求的验证规则。
*
* @return array
*/
public function rules()
{
return [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
];
}
验证规则是如何运行的呢?你所需要做的就是在控制器方法中类型提示传入的请求。在调用控制器方法之前验证传入的表单请求,这意味着你不需要在控制器中写任何验证逻辑:
/**
* 保存传入的博客文章。
*
* @param StoreBlogPost $request
* @return Response
*/
public function store(StoreBlogPost $request)
{
// The incoming request is valid...
}
如果验证失败,就会生成一个让用户返回到先前的位置的重定向响应。这些错误也会被闪存到 Session 中,以便这些错误都可以在页面中显示出来。如果传入的请求是 AJAX,会向用户返回具有 422 状态代码和验证错误信息的 JSON 数据的 HTTP 响应。
添加表单请求后钩子
如果你想在表单请求「之后」添加钩子,可以使用 withValidator
方法。这个方法接收一个完整的验证构造器,允许你在验证结果返回之前调用任何方法:
/**
* 配置验证器实例。
*
* @param \Illuminate\Validation\Validator $validator
* @return void
*/
public function withValidator($validator)
{
$validator->after(function ($validator) {
if ($this->somethingElseIsInvalid()) {
$validator->errors()->add('field', 'Something is wrong with this field!');
}
});
}
授权表单请求
表单请求类内也包含了 authorize
方法。在这个方法中,你可以检查经过身份验证的用户确定其是否具有更新给定资源的权限。比方说,你可以判断用户是否拥有更新文章评论的权限:
/**
* 判断用户是否有权限做出此请求。
*
* @return bool
*/
public function authorize()
{
$comment = Comment::find($this->route('comment'));
return $comment && $this->user()->can('update', $comment);
}
由于所有的表单请求都是继承了 Laravel 中的请求基类, 所以我们可以使用 user
方法去获取当前认证登录的用户。同时请注意上述例子中对 route
方法的调用。这个方法允许你在被调用的路由上获取其定义的 URI 参数,譬如下面例子中的 {comment}
参数:
Route::post('comment/{comment}');
如果 authorize
方法返回 false
,则会自动返回一个包含 403 状态码的 HTTP 响应,也不会运行控制器的方法。
如果你打算在 应用程序的其它部分也能处理授权逻辑,只需从 authorize
方法返回 true
:
/**
* 判断用户是否有权限进行此请求。
*
* @return bool
*/
public function authorize()
{
return true;
}
自定义错误消息
你可以通过重写表单请求的 messages
方法来自定义错误消息。此方法应该如下所示返回属性/规则对数组及其对应错误消息:
/**
* 获取已定义的验证规则的错误消息。
*
* @return array
*/
public function messages()
{
return [
'title.required' => 'A title is required',
'body.required' => 'A message is required',
];
}