本文译自 Matt Stauffer 的系列文章.
只要你曾经在使用 Laravel 框架的过程中试图找到有关用户输入验证的最佳实践, 你就应该了解这是一个争论最多并且几乎没有达成共识的话题. 我们可以在控制器中进行验证, 可以在单独的一个服务层进行验证, 可以在模型中进行验证, 当然还可以在 Javascript 中进行验证 (这只是一个玩笑, 谁都知道不能只依赖于客户端的验证). 但是, 哪一种做法才是最佳的呢?
Laravel 5.0 新引入的表单请求 (Form Request) 特性提供了集规范性 (差不多就是 "最佳实践" 的意思) 和便捷性 (这是比之前任何一种选择都更强大也更便捷的方式) 于一体的, 在 Laravel 中执行数据检查和验证的新手段.
说明: 本文中使用新的
view()
辅助方法代替了旧版本中的View::make()
.
Laravel 5.0 带来了 Form Requests, 这是一种特殊的类型, 用于在提交表单时进行数据的检查和验证. 每个 Form Request 类至少包含一个 rules()
方法, 这个方法返回一组验证规则. 除此之外还必须包含一个 authorize()
方法, 该方法返回一个布尔值, 代表是否允许用户执行本次请求.
Laravel 会在解析 POST 路由之前自动把用户输入的信息传递给相应的表单请求, 因此我们的所有验证逻辑都可以移到独立于控制器和模型之外的 FormRequest 对象中.
如果你还没有创建好的 Laravel 5.0 项目, 用下面的命令创建一个:
$ composer create-project laravel/laravel myProject dev-develop --prefer-dist
// app/Http/routes.php
Route::get('/', 'FriendsController@getAddFriend');
Route::post('/', 'FriendsController@postAddFriend');
//app/Http/Controllers/FriendsController:
namespace App\Http\Controllers;
use App\Http\Requests\FriendFormRequest;
use Illuminate\Routing\Controller;
use Response;
use View;
class FriendsController extends Controller
{
public function getAddFriend()
{
return view('friends.add');
}
public function postAddFriend(FriendFormRequest $request)
{
return Response::make('Friend added!');
}
}
<html><body>
@foreach ($errors->all() as $error)
<p class="error">{{ $error }}</p>
@endforeach
<form method="post">
<label>First name</label><input name="first_name"><br>
<label>Email address</label><input name="email_address"><br>
<input type="submit">
</form>
</body></html>
// app/http/requests/FriendFormRequest.php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Response;
class FriendFormRequest extends FormRequest
{
public function rules()
{
return [
'first_name' => 'required',
'email_address' => 'required|email'
];
}
public function authorize()
{
// 只允许登陆用户
// 返回 \Auth::check();
// 允许所有用户登入
return true;
}
// 可选: 重写基类方法
public function forbiddenResponse()
{
// 这个是可选的, 当认证失败时返回自定义的 HTTP 响应.
// (框架默认的行为是带着错误信息返回到起始页面)
// 可以返回 Response 实例, 视图, 重定向或其它信息
return Response::make('Permission denied foo!', 403);
}
// 可选: 重写基类方法
public function response()
{
// 如果需要自定义在验证失败时的行为, 可以重写这个方法
// 了解有关基类中这个方法的默认行为,可以查看:
// https://github.com/laravel/framework/blob/master/src/Illuminate/Foundation/Http/FormRequest.php
}
}
接下来, 用 php artisan serve
或者你自己喜欢的方式启动服务器. 提交表单, 你可以看到我们并没有往控制器中添加任何一行验证逻辑, 但是验证规则已经生效了.
如果对 "新增" 和 "编辑" 有不同的规则, 或者根据不同的输入进行不同的验证, 要怎么办呢? 这里有几个可以参考的例子, 虽然还不能确定这些就是 "最佳实践":
Laravel 并没有规定你不能对 "新增" 和 "编辑" 操作采用不同的 form request 类. 所以你可以创建一个包含所有规则的 FriendFormRequest 作为基类, 然后把它扩展为 addFriendFormRequest 和 editFriendFormRequest 两个子类, 每个子类都可以实现各自的默认行为.
rules()
作为一个方法而不是属性, 带来的好处就是你可以在方法中添加判断逻辑:
...
class UserFormRequest extends FormRequest
{
...
protected $rules = [
'email_address' => 'required',
'password' => 'required|min:8',
];
public function rules()
{
$rules = $this->rules;
// 根据不同的情况, 添加不同的验证规则
if ($someTestVariableShowingThisIsLoginInsteadOfSignup)
{
$rules['password'] = 'min:8';
}
return $rules;
}
}
也可以在 authorize
方法中添加逻辑, 比如:
...
class FriendFormRequest extends FormRequest
{
...
public function authorize()
{
if ( ! Auth::check() )
{
return false;
}
$thingBeingEdited = Thing::find(Input::get('thingId'));
// 如果是编辑操作, 或者当前用户不是对象创建者
if ( ! $thingBeingEdited || $thingBeingEdited->owner != Auth::id()) {
return false;
}
return true;
}
}
除了上面的方式, 如果需要对验证逻辑进行更深入的控制, 可以重写提供校验对象实例的方法. 下面是一个简单的实例, 后续会专门写一篇文章来解释:
...
class FriendFormRequest extends FormRequest
{
public function validator(ValidationService $service)
{
$validator = $service->getValidator($this->input());
// 可选: 通过新的 ->after() 方法来进行自定义
$validator->after(function() use ($validator)) {
// 在这里可以做更多更深入的校验
$validator->errors()->add('field', 'new error);
}
}
}
后续还会有一篇有关 ValidatesWhenResolved 接口的文章, 不过那篇文章重点讨论的是对方法/路由等的校验. IOC 何时提供什么东西, 这个在 Laravel 5.0 版已经分离出一个单独的接口. 官方文档: https://github.com/illuminate/contracts/blob/master/Validation/ValidatesWhenResolved.php
$redirect
: 校验失败时要重定向到的 URI.$redirectRoute
: 校验失败时要重定向到的路由.$redirectAction
: 校验失败时要重定向到的方法.$dontFlash
: 重定向时不要传递的输入项的键 (默认值: ['password', 'password_confirmation']).通过文本可以看到, Form Requests 对于简化表单请求的数据校验是非常强大和方便的. 如果你阅读本文觉得还不够, 可以观看关于 Form Request 的这个视频.
本文写作时, Laravel 5.0 还未正式发布, 因此上述内容最终可能还会有修改, 或者作者遗漏了某些东西. 如果你有建议或者对文章内容的修正, 可以在 给译者发邮件 或者在 Twitter 上直接联系原作者.