[Laravel]ミドルウェアを整理してLaravelを軽くする

[ディップ Advent Calendar](http://qiita.com/advent-calendar/2016/dip-dev) の最終日の記事です。

本記事では、Laravel5 にデフォルトで設定されている Middleware について書きます。
どんな機能がデフォルトで動くようになっているかを知ることで、Laravel アプリケーションをより効率的で高速に動かすことができるかもしれないという話です。

- [Middleware - Laravel - The PHP Framework For Web Artisans](https://laravel.com/docs/5.3/middleware)（Laravel 公式サイト）
- [ミドルウェア 5.3 Laravel](https://readouble.com/laravel/5.3/ja/middleware.html)（日本語訳）

## Laravel に含まれるミドルウェア
Laravel5.3 においては、下記の Middleware がデフォルトで設定されています。

```app/Http/Kernel.php
...
    protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
    ];

...

    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            'throttle:60,1',
            'bindings',
        ],
    ];

...

    protected $routeMiddleware = [
        'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
    ];

...
```

各ミドルウェアの機能を簡単に見ていきましょう。

### $middleware
HTTP リクエストをもとに Laravel が生成する Request インスタンスに対して**必ず実行される処理**です。Laravel5.3 では1つだけ定義されています。

- CheckForMaintenanceMode
 - Laravel にはメンテナンスモードに切り替える機能があります。このミドルウェアはシステムがメンテナンスモードにあるかどうかを判断します。  
[Configuration#Maintenance Mode - Laravel - The PHP Framework For Web Artisans](https://laravel.com/docs/5.3/configuration#maintenance-mode)

### $middlewareGroups
複数のミドルウェアをまとめて適用したい場合、こちらに記述します。この機能は Laravel5.2 で追加されました。
デフォルトではWebサイト向けとAPI利用向けにそれぞれ「Web」「api」グループが定義されています。

#### [Web]グループ
- \App\Http\Middleware\EncryptCookies::class
 - Cookie の暗号化/復号化処理を行う機能
- \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class
 - 指定された Cookie を Response ヘッダに登録する機能
- \Illuminate\Session\Middleware\StartSession::class
 - Session の生成や取得などを行う機能
- \Illuminate\View\Middleware\ShareErrorsFromSession::class
 - Session に保存されたエラー情報を View にセットする機能
- \App\Http\Middleware\VerifyCsrfToken::class
 - CSRF を防ぐためのトークンの発行やチェックを行う機能
- \Illuminate\Routing\Middleware\SubstituteBindings::class
 - Route Model Binding を実行する機能（Laravel5.3から追加）

公式にも以下の通り書いてありますが route/web.php には、この`web`グループが自動的に適用されるようになっています。つまり`$middleware`と`$middlewareGroups`の`web`グループに定義されたミドルウェアの処理がすべて実行されるということです。
> Out of the box, the web middleware group is automatically applied to your routes/web.php file by the RouteServiceProvider.
> *https://laravel.com/docs/5.3/middleware#middleware-groups*

#### [api]グループ
- throttle:60,1
 - 後述の $routeMiddleware の throttle を参照
- bindings
 - 後述の $routeMiddleware の bindings を参照

### $routeMiddleware
特定の処理だけ適用するミドルウェアはこちらにキー名とセットで定義して使いやすくしておきます。Route や Controller で下記のように指定して使用します。

```route.php
Route::get('/', function () {
    //
})->middleware('auth');
```

デフォルトで定義されている Middleware は以下の通りです。認証系が多いです。

- auth（\Illuminate\Auth\Middleware\Authenticate::class）
 - 特定のルートへのアクセスに対してユーザー認証を行う機能
- auth.basic（\Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class）
 - 特定のルートへのアクセスに対して Basic 認証を行う機能
- bindings（\Illuminate\Routing\Middleware\SubstituteBindings::class）
 - Route Model Binding を実行する機能（Laravel5.3から追加）
- can（\Illuminate\Auth\Middleware\Authorize::class）
 - 特定のモデルやリソースへのアクションに対してポリシーを付与する機能（Laravel5.3から参照先変更）
- guest（\App\Http\Middleware\RedirectIfAuthenticated::class）
 - 認証済かチェックし、認証済であれば /home にリダイレクトする機能
- throttle（\Illuminate\Routing\Middleware\ThrottleRequests::class）
 - 同一ユーザーが単位時間内に規定回数以上のアクセスを行ったかどうかチェックする機能

## 使うミドルウェアを見極める
で、Laravel をインストールして使い始める場合、Route や Config まわりを一通り設定したら、まずはあらかじめ用意されているファサードや Eloquent、Blade テンプレートなどを使ってビジネスロジックを作っていくケースが多いのではと思います。

その場合、Kernel.php の存在に気づくのは、作り始めてしばらく経ってからなのではないでしょうか。Kernel.php をデフォルトのまま使うということは、特に意識せずとも`$middleware`や`$middlewareGroups`に記載されたミドルウェアの機能を経由したうえでビジネスロジックを動かすということになります。

そして前述したように、アプリケーションによっては不要になるミドルウェアがあることに気付きます。メンテナンス画面がそもそも不要だったり、メンテナンス中のアクセスは別のサーバに飛ばすような仕組みがあれば`CheckForMaintenanceMode`は使いません。Session が不要なアプリケーションもあるかもしれませんし、不正な集中アクセスを防ぐ仕組みをファイヤーウォールに任せるケースもあると思いますので、その処理を担うミドルウェアを動作させる必要はなくなります。

使わないミドルウェアは Kernel.php から削除してしまって良いです。あるいは、事前/事後処理が必要なケースのときだけ`$routeMiddleware`に定義して使うとか。

ミドルウェア設定を見直し不要な処理を除外することで、Laravel アプリケーションの動作速度が向上する場合があります。Kernel.php の中身をあまり変えずに作ってきた方は試してみてください。