LoginSignup
0
1

More than 1 year has passed since last update.

Laravelアプリケーション学習⑫(ミドルウェア)

Posted at

本記事ではLaravelのアプリケーションを理解し、より良い設計・アーキテクチャを構築できるように学習したことを簡潔にまとめています。

目次

1.Laravelのアーキテクチャ
2.アプリケーションのアーキテクチャ
3.HTTPリクエストとレスポンス
4.データベース
5.認証と許可
6.イベントとキューによる処理の分離
7.コンソールアプリケーション
8.テスト
9.エラーハンドリングとログの活用
10.テスト駆動開発の実践

3.HTTPリクエストとレスポンス

3.4 ミドルウェア

Laravelのミドルウェアはコントローラクラスの処理前後に位置し、主にHTTPリクエストのフィルタリングやHTTPレスポンスの変更を担う。
Laravelで提供されているミドルウェアは以下の3種類

1.システム全体で使用するミドルウェア(グローバルミドルウェア)
2.特定のルートに適用されるミドルウェア(ルートミドルウェア)
3.コントローラクラスのコンストラクタで指定するミドルウェア(コンストラクタ内ミドルウェア)

HTTPリクエストがコントローラのアクションメソッドに到達するまでに、グローバルミドルウェア、ルートミドルウェア、コンストラクタ内ミドルウェアの順に通過する。
コントラクタのアクションメソッドからレスポンスが返されるとき、再びミドルウェアを通るため、必要に応じてレスポンス内容の変更や新たなレスポンスの生成が可能である。
これらの処理はIlluminate\Pipeline\Pipelineクラスがそれぞれのミドルウェアを実行する

デフォルトで用意されているミドルウェア

laravelでは標準で多数のミドルウェアが用意されており、app/Http/kernel.phpのapp\Http\Kernerクラスで定義されている。ミドルウェアは指定された順番で実行されるので注意が必要。

グローバルミドルウェア

グローバルミドルウェアは、ルーターに登録されたコントローラクラスが動作する前に実行される。
→グローバルミドルウェアではルート情報を取り扱う処理ができない

下記にグローバルミドルウェアを示す。

1 2
ミドルウェア 概要
App\Http\Middleware\PreventRequestsDuringMaintenance メンテナンスの場合のふるまいを変更することができ、すべてアクセスに対してメンテナンス画面を表示、または特定のURIをメンテナンスから除外
Illuminate\Foundation\Http\Middleware\ValidatePostSize リクエストボディのサイズをチェックし、この値が不正な場合はIlluminate\Http\Exception\PostTooLargeExceptionをスローする。
Illuminate\Foundation\Http\Middleware\ConvertEmptyStringToNull リクエストの中で空文字をすべてNULLに変更
App\Http\Middleware\TrimStrings リクエスト文字列に対してtrim処理を行い空文字を削除する。exceptプロパティにこの処理を除外したいリクエストパラメータを指定できる
App\Http\Middleware\TrustProxies リバースプロキシーやロードバランサを使用している環境で、アプリケーション内でHTTPSのリンクなどが生成されない場合、信頼できるipなどproxiesプロパティの配列で追記することで作用する。CDNなどを利用する場合はこのミドルウェアに記述する必要がある
Fritcake\Cors\HandleCors LaravelでAPIを提供する際、異なるドメインからJavascriptなどを介したコールでHTTPリクエストを制限する。Apache HTTP ServierやNginxなどでCORSを設定している場合は不要
App\Http\Middleware\TrustHosts 信頼できるホストを記述し、それ以外のホストからのアクセスを制限する。単一のWebサーバで単一サイトをホストしている場合は不要だが、1つのアプリケーション内でサブドメインなどで分割しているケースなどで利用できる

ルートミドルウェア

デフォルトではwebミドルウェアグループに記述されている。下記にルートミドルウェアを示す。

1 2
ミドルウェア *** 概要***
App\Http\Middleware\EncryptCookies クッキーの暗号化及び復号を行う。他のアプリケーションで発行されたクッキーは復号できないため、復号対象から除外したいクッキーをexceptプロパティの配列で指定する
App\Http\Middleware\VertifyCsrfToken CSRF対策のXSRF-TOKEN発行やトークンのチェックを行う。HEADリクエスト、GETリクエスト、OPTIONSリクエスト以外を対象に動作する。対象から外したい場合はURIをexceptプロパティで指定する。
Illuminate\Coockie\Middleware\AddQueuedCookiesToResponse Cookie::queueで登録した値をレスポンスにクッキーとして追加して返却する。
Illuminate\Session\Middleware\StartSession セッションを有効にし、レスポンス返却時にセッションに書き込む。
Illuminate\Session\Middleware\AuthenticateSession パスワード変更時に他のデバイスでログインしている、対象のユーザーをログアウトさせる
Illuminate\View\Middleware\ShareErrorsFromSession Bladeテンプレートのerrors変数に、セッションのerrorsキーから取得したエラー内容を埋め込む
Illuminate\Routing\Middleware\SubstituteBindings Eloquentモデルと結合させて、ルートに利用されるidなどからデータベース検索を行い、コントローラクラスやルートで利用可能にする。この機能はRoute Model Bindingと呼ばれる

名前付きミドルウェア

ルーターへの登録またはコントローラクラスのコンストラクタなどで任意の名前で指定して利用できる
下記に名前付きミドルウェアを示す。

1 2 3
ミドルウェア名 ミドルウェアクラス 概要
auth Illuminate\Auth\Middleware\Authenticate 認証済みユーザーかどうかを判定し、認証済みユーザーでない場合、Illuminate\Auth\AuthenticationExceptionがスローされる
auth.basic Illumiante\Auth\Middleware\AuthenticateWithBasicAuth Basic認証を行う
bindings Illuminate\Routing\Middleware\SubstituteBindings グローバルミドルウェアで記述されているものと同一のミドルウェア
cache.headers Illuminate\Http\Middleware\Authorize ETag(エンティティタグ)を利用しコンテンツのキャッシュを制御します。
can Illuminate\Auth\Middleware\Authorize 特定のモデルやリソースへのアクションに対して認可を行う。この機能は認可機能と組み合わせて使う
throttle Illuminate\Routing\Middleware\hrottleRequests 同一ユーザーが単位時間内に規定回数以上のアクセスを行ったかどうか判定する。規定回数以上アクセスした場合はIlluminate\Http\Exceptions\ThrottleRequestsExceptionがスローされる
signed Illuminate\Routing\Middleware\ValidateSignature 署名付きのアクセスで有効な署名かどうか、制限時間内のアクセスかどうかを判定する
guest App\Http\Middleware\RedirectlfAuthenticated 認証済みかどうかを判定し、認証済みの場合は/homeにリダイレクトする。リダイレクト先が固定されているので、アプリケーションに合わせたミドルウェアを用意する必要がある。
verified Illumiante\Auth\Middleware\EnsureEmailsVerified メールアドレス認証済みユーザーのみにアクセスを許可をするルートミドルウェア。利用にはIlluminate\Contracts\Auth\MustVerifyEmailインターフェースを定義したクラスをauthドライバ経由で利用しなればならない。

このようにlaravelでは標準で様々なミドルウェアが準備されているが、不要なミドルウェアは除外しておくと、パフォーマンスが向上するケースがあるので、利用するミドルウェアを事前に調査しておいた方がよい。

独自のミドルウェアの実装

本記事では、リクエストヘッダとレスポンスヘッダをログに書き出す独自のミドルウェアを実装していく。なお、グローバルミドルウェアやルートミドルウェアに実装の違いはなく、どのタイミングで実行させるかを指定するだけである。

 ①ミドルウェアクラスの生成

リクエストヘッダとレスポンスヘッダをログに書き出すミドルウェアとして、HeaderDumperクラスを作成する。

php artisan make:middleware HeaderDumper

app/Http/Middlewaresディレクトリ配下にHeaderDumperクラスが生成される。

②リクエストヘッダログの出力

作成したクラスにリクエストヘッダのログを出力するように変更する。

<?php
declare(strict_types=1)

namespace App\Http\Middleware;

use Closure;
use Illumiate\Http\Request;
use Psr\Log\LoggerInterface;

use function info;
use function strval; // 文字列に変換する関数

final class HeaderDumper
{
   private $logger;

   public function __construct(LoggerInterface $logger)
   {
      $this->logger = $logger;
   }

   public function handle(Request $request, Closure $next)
   {
      $this->logger->info('request', ['header' => strval($request->headers)]);

      // ヘルパー関数を利用する場合は以下の通り
      // info('request', ['header' => strval($request->headers)]);
     return $next($request);
   }
}

<コード解説>
上記コードに示す通り、リクエストインスタンスがhandleメソッドに渡された場合に、headersプロパティにアクセスしてリクエストヘッダを取得する。このプロパティはSymfony\Component\HttpFoundation\HeaderBagインスタンスとなるので、文字列にキャストするか、__toStringメソッドを用いてことで、リクエストヘッダを文字列で取得する。
取得した文字列をログに書き込むために、コンストラクタインジェクションでPsr\Log\LoggerInterfaceを読み込んでいる。(infoヘルパー関数でも可能。その場合Psr\Log\LoggerInterfaceは不要)

③レスポンスヘッダのログ出力

レスポンスヘッダを取得するには、ミドルウェアクラス内のhandleメソッドに記述されている$next($request)の戻り値を取得する。
ミドルウェアクラス以降に順次ミドルウェアとコントローラクラスを実行し、その戻り値が返却される。
レスポンスヘッダはリクエストヘッダと同様にheadersプロパティにアクセスし、インスタンスを文字列にキャストすることで取得する。

public function (Request $request, Closure $next)
{
   $this->logger->info('requet', ['header' => strval($request->headers)]);

   $response = $next($request);
   $this->logger->info('response', ['header' => strval($response->headers)]);

   return $response;
}

④ミドルウェアの登録

ミドルウェアの登録はApp\Http\kernelクラスに追記する。
グローバルミドルウェアで最初に動作するようにすることで(先頭に記載)、最後に出力されるレスpん巣を取得可能にしている。
下記にKernel.phpを示す。

<?php
declare(strict_types=1);

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as Httpkernel;

class Kernel extends HttpKernel
{
   protected $middleware = [  // ①
      \App\Http\Middleware\HeaderDumper::class, // 作成したミドルウェアを追記
      \Illuminate\Foundation\Http\Middleware\CheckFormaintenanceMode::class,
      --[]--
   ];

   protected $middlewareGroups = [ // ②
      --[]--
   ];

   protected $routeMiddleware = [ // ③
      --[]--
   ];
}

<コード解説>
①グローバルミドルウェアの定義はmiddlewareプロパティの配列に文字列でクラス名を定義する。全アクセスに対して適用したいミドルウェアはここに定義する。
②複数のミドルウェアをまとめて扱いたい場合は、middlewareGroupsプロパティにグループ名とともに文字列でクラス名を定義する。
③特定のルートやコントローラに対してミドルウェアを適用する場合は、routeMiddlewareプロパティにキー名とともにクラス名を定義する。

なお、標準で登録されている「web」グループと「api」グループは、ルートの定義のroute/web.phpを通る処理に対しては、必ず「web」グループのミドルウェアが適用され、route/api.phを通る処理に対しては、「api」グループが適用される。
これは、App\Providers\RouteServiceProviderクラスに記述されており、アプリケーションに合わせてミドルウェアが属数グループを変更したい場合は、RouteServiceProviderクラス内の指定を変更すればよい。

0
1
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
1