55.重构提交回复
- 本系列文章为
laracasts.com
的系列视频教程——Let's Build A Forum with Laravel and TDD 的学习笔记。若喜欢该系列视频,可去该网站订阅后下载该系列视频, 支持正版 ;- 视频源码地址:github.com/laracasts/Lets-Build-a-...;
- 本项目为一个 forum(论坛)项目,与本站的第二本实战教程 《Laravel 教程 - Web 开发实战进阶》 类似,可互相参照。
本节说明
- 对应视频教程第 55 小节:Refactoring to Form Requests
本节内容
就现在而言,处理提交新回复的方法看上去仍然有点臃肿,所以在本节中,我们将它重构为一个方法,然后再进行调用。我们将使用 表单请求验证 来进行我们的重构。首先我们创建表单请求类:
$ php artisan make:request CreatePostRequest
修改内容如下:
forum\app\Http\Requests\CreatePostRequest.php
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class CreatePostRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'body' => 'required|spamfree', ]; } }
注:我们暂时让
authorize()
方法返回true
,并且将验证规则放在了rules()
方法中。
现在我们的代码可以进行如下修改:
forum\app\Http\Controllers\RepliesController.php
. . public function store($channelId,Thread $thread,CreatePostRequest $request) { if(Gate::denies('create',new Reply)) { return response( 'You are posting too frequently.Please take a break.:)',422 ); } $reply = $thread->addReply([ 'body' => request('body'), 'user_id' => auth()->id(), ]); return $reply = $thread->addReply([ 'body' => request('body'), 'user_id' => auth()->id(), ])->load('owner'); } . .
我们需要修改测试:
forum\tests\Feature\ParticipateInForumTest.php
. . /** @test */ public function replies_that_contain_spam_may_not_be_created() { $this->withExceptionHandling(); $this->signIn(); $thread = create('App\Thread'); $reply = make('App\Reply',[ 'body' => 'something forbidden' ]); $this->post($thread->path() . '/replies',$reply->toArray()) ->assertStatus(422); } . .
运行测试:
我们需要对异常进行处理:
forum\app\Exceptions\Handler.php
. . public function render($request, Exception $exception) { if($exception instanceof ValidationException){ return response('Validation failed.',422); } return parent::render($request, $exception); } . .
如果是ValidationException
异常,我们返回 422。因为我们已经对异常进行了处理,再次运行测试:
接下来我们来整理我们的授权策略:
forum\app\Http\Requests\CreatePostRequest.php
<?php namespace App\Http\Requests; use App\Exceptions\ThrottleException; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Gate; class CreatePostRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return Gate::allows('create',new \App\Reply); } protected function failedAuthorization() { throw new ThrottleException; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'body' => 'required|spamfree', ]; } }
我们重写failedAuthorization()
,并且定义了一个异常类:ThrottleException
。这个异常类现在还没有建立,我们前往建立:
forum\app\Exceptions\ThrottleException.php
<?php namespace App\Exceptions; class ThrottleException extends \Exception { }
同样地,我们要为这个异常类做处理:
forum\app\Exceptions\Handler.php
. . public function render($request, Exception $exception) { if($exception instanceof ValidationException){ return response('Validation failed.',422); } if($exception instanceof ThrottleException){ return response('You are posting too frequently.',429); } return parent::render($request, $exception); } . .
既然我们已经将授权策略转移了位置,那么我们的控制器代码就可以进行修改了:
forum\app\Http\Controllers\RepliesController.php
. . public function store($channelId, Thread $thread, CreatePostRequest $request) { return $reply = $thread->addReply([ 'body' => request('body'), 'user_id' => auth()->id(), ])->load('owner'); } . .
我们还需要修改我们的测试:
forum\tests\Feature\ParticipateInForumTest.php
. . /** @test */ public function users_may_only_reply_a_maximum_of_once_per_minute() { $this->withExceptionHandling(); $this->signIn(); $thread = create('App\Thread'); $reply = make('App\Reply',[ 'body' => 'My simple reply.' ]); $this->post($thread->path() . '/replies',$reply->toArray()) ->assertStatus(200); $this->post($thread->path() . '/replies',$reply->toArray()) ->assertStatus(429); } } . .
如果你观察得足够仔细,你会发现对授权策略的状态码返回,我们本节用的是 429,而上一节用的是 422。这是上一节我们的一个小错误,我们在本节进行修正。如果我们不清楚状态码的具体含义,我们可以在下面的文件中去确认状态码,例如我们现在需要的状态码 429,表示的是 HTTP_TOO_MANY_REQUESTS:
forum\vendor\symfony\http-foundation\Response.php
现在我们来运行全部测试:
我们的测试已经通过,但是如果你新建一个不合法的话题:
这是因为我们直接抛出了异常,而没有区分应用场合。我们进行下修改:
forum\app\Exceptions\Handler.php
. . public function render($request, Exception $exception) { if($exception instanceof ValidationException){ if ($request->expectsJson()){ return response('Validation failed.',422); } } if($exception instanceof ThrottleException){ return response('You are posting too frequently.',429); } return parent::render($request, $exception); } . .
如果是Ajax
调用,我们直接抛出异常;否则,我们不做处理。我们再次尝试:
我们再次运行全部测试:
修复未通过的测试:
. . /** @test */ public function replies_that_contain_spam_may_not_be_created() { $this->withExceptionHandling(); $this->signIn(); $thread = create('App\Thread'); $reply = make('App\Reply',[ 'body' => 'something forbidden' ]); $this->json('post',$thread->path() . '/replies',$reply->toArray()) ->assertStatus(422); } . .
再次运行全部测试:
推荐文章: