LoginSignup
0
0

More than 5 years have passed since last update.

複数のHandlerクラスを使い分ける

Last updated at Posted at 2017-05-12

背景

web画面とAPI2つを同時に提供しようしたときに、例外検知時の基本ルールとして

  • web画面側:エラーページに遷移させたい(エラーページ用のhtmlを返却
  • API側:error情報を格納したJSONを返却したい

とやりたいことが大きく異なりました。Handlerクラス内でif文をチマチマ書くのがしっくり来なかったのでいっその事Handlerクラスを切り替えることは出来ないかなーというのがきっかけでした。

実装方法

コンテナに登録されているExceptionHandlerをサービスプロバイダーで登録し直します。
登録し直すかどうかはURLを条件に判定します。アプリケーション固有のURL直後にapi/が続く場合はAPI用のHandlerクラスを使用し、それ以外は通常のHandlerクラスを使用します。
※この判定ルールは各々の環境に合わせて適宜書き換えてください。

AppServiceProvider.php
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot() {
        // API関連は独自のHandlerクラスをコンテナへ再登録する
        if (strpos(\Request::getPathInfo(), '/api/') === 0) {
            $this->app->bind(ExceptionHandler::class, \App\Exceptions\ApiHandler::class);
        }
    }

サンプルとしてModelNotFoundExceptionがスローされた場合にerrorオブジェクトをJSON形式で返却させてみます。QueryBuilderのfindOrFailなどを呼び出して対象レコードが見つからなかった場合が下記分岐に当てはまります。
それ以外のfunctionはデフォルトのHandlerクラスをそのままコピーしています。

ApiHandler.php
class ApiHandler extends ExceptionHandler {
    /**
     * Prepare response containing exception render.
     *
     * {@inheritdoc}
     *
     * @param \Illuminate\Http\Request $request
     * @param \Exception $e
     * @return \Symfony\Component\HttpFoundation\Response
     * @see \Illuminate\Foundation\Exceptions\Handler::prepareResponse()
     */
    protected function prepareResponse($request, Exception $e) {
        if ($e->getPrevious() instanceof ModelNotFoundException) {
            return response()->json([
                    'error' => [
                            'code' => 9999
                            'message' => '対象のデータが見つかりませんでした。'
                    ]
            ], 404);
        }
    }
}

残課題

今の状況だとphpunitを使用してテストを行ったときにApiHandlerが使用されないという問題が発生しています。
対応方法を確認次第追記する予定です。

(2017/05/17追記)

原因は以下のような感じでした。
1. phpunitを通してアプリケーションの初期化
2. テストクラスのインスタンス生成
3. AppServiceProviderが実行される
4. URLをもとにHandlerクラスを振り分ける
5. テストメソッド単位で順次実行
6. MakesHttpRequests::call()でHttpRequestを発生させる
→ここで初めて~/api/の様なURLが確定するので、手順3 の段階ではURLが空っぽになっているためApiHandlerをサービスコンテナに再登録する処理が実行されない。

API関連のテストクラスの親に当たるクラスを新たに作成し、setUpメソッド内でサービスコンテナにHandlerクラスを再登録させることで回避しました。

ApiTestCase.php
<?php
namespace Tests;

use Tests\TestCase;

class ApiTestCase extends TestCase {

    public function setUp() {
        parent::setUp();
        $this->app->bind(\Illuminate\Contracts\Debug\ExceptionHandler::class, \App\Exceptions\ApiHandler::class);
    }
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0