让人头痛的表单验证
本文译自 Matt Stauffer 的 系列文章 .
只要你曾经在使用 Laravel 框架的过程中试图找到有关用户输入验证的最佳实践, 你就应该了解这是一个争论最多并且几乎没有达成共识的话题. 我们可以在控制器中进行验证, 可以在单独的一个服务层进行验证, 可以在模型中进行验证, 当然还可以在 Javascript 中进行验证 (这只是一个玩笑, 谁都知道不能只依赖于客户端的验证). 但是, 哪一种做法才是最佳的呢?
Laravel 5.0 新引入的表单请求 (Form Request) 特性提供了集规范性 (差不多就是 "最佳实践" 的意思) 和便捷性 (这是比之前任何一种选择都更强大也更便捷的方式) 于一体的, 在 Laravel 中执行数据检查和验证的新手段.
说明: 本文中使用新的 view()
辅助方法代替了旧版本中的 View::make()
.
Form Requests 使表单验证不再让人头痛
Laravel 5.0 带来了 Form Requests, 这是一种特殊的类型, 用于在提交表单时进行数据的检查和验证. 每个 Form Request 类至少包含一个 rules()
方法, 这个方法返回一组验证规则. 除此之外还必须包含一个 authorize()
方法, 该方法返回一个布尔值, 代表是否允许用户执行本次请求.
Laravel 会在解析 POST 路由之前自动把用户输入的信息传递给相应的表单请求, 因此我们的所有验证逻辑都可以移到独立于控制器和模型之外的 FormRequest 对象中.
开始实践: 快速创建一个 Laravel 5.0 项目
如果你还没有创建好的 Laravel 5.0 项目, 用下面的命令创建一个:
$ composer create-project laravel/laravel myProject dev-develop --prefer-dist
1. 添加路由
// app/Http/routes.phpRoute::get('/', 'FriendsController@getAddFriend');Route::post('/', 'FriendsController@postAddFriend');
2. 创建控制器
//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!'); } }
3. 创建视图
<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>
4. 创建 FormRequest
// app/http/requests/FriendFormRequest.phpnamespace 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
或者你自己喜欢的方式启动服务器. 提交表单, 你可以看到我们并没有往控制器中添加任何一行验证逻辑, 但是验证规则已经生效了.
其它用例
如果对 "新增" 和 "编辑" 有不同的规则, 或者根据不同的输入进行不同的验证, 要怎么办呢? 这里有几个可以参考的例子, 虽然还不能确定这些就是 "最佳实践":
采用分开的 form requests
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 接口
后续还会有一篇有关 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 上直接联系原作者)
[http://twitter.com/stauffermatt].