【Laravel】ミドルウェアとは?使い方やメリット、グローバル登録の方法
この記事からわかること
- LaravelのMiddleware(ミドルウェア)とは?
- 使い方や設定方法、メリット
- ビューコンポーザとの違い
- 実行されるタイミング
- グローバルミドルウェアとミドルウェアのグループ化
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
phpのフレームワークであるLaravelの機能の1つ「Middleware(ミドルウェア)」の仕組みや設定方法、使い方など備忘録がてらまとめていきたいと思います。
Middleware(ミドルウェア)とは?
Middleware(ミドルウェア)とはLaravelに備わっているHTTPリクエストが送られたタイミングで実行される処理を定義できる機能です。
LaravelではURLにアクセス(リクエスト)があるとマッチするルーティングを検索し、そのルーティングに紐づけられているコントローラのアクションメソッドを呼び出し指定されたレスポンスを返します。
URL(リクエスト)
↓
ルーティング
↓ ⇦ ミドルウェア処理
アクションメソッド
↓ ⇦ (orミドルウェア処理)
レスポンス作成
↓ ⇦ ビューコンポーザ処理
レンダリング(ページ表示)
ミドルウェアはリクエストを受けてからコントローラのアクションメソッドが実行される前部分(または後部分)に処理を行わせることができます。
例えばページアクセスに認証機能を持たせる場合などそのページのレスポンスが返る前にログイン済みかを識別し、済みであればそのページを未ログインであれば別ページのリダイレクトを施すといった際にミドルウェアは力を発揮します。
Middleware(ミドルウェア)とは〜まとめ〜
- HTTPリクエストが送られたタイミングで実行される処理を定義できる機能
- コントローラ実行前(実行後)に処理を挟める
- 認証機能などに使われている
ビューコンポーザとの違いとメリット
Laravelには似たような機能の「ビューコンポーザ」があります。個人的に両者の違いが分かりにくかったのでまとめてみます。
ビューコンポーザの特徴
- 自動実行される処理
- Webページをレンダリングする際に実行
- bladeファイルごとに紐付け
- レスポンスとレンダリングの間に挟める処理
- ファイルはクラスで定義
- 管理場所は「app」>「Http」>「View」>「Composer」内がオススメ
- サービスとサービスプロバイダと深い関係
- 作成コマンド:php artisan make:provider TestServiceProvider
ミドルウェアの特徴
- 自動実行される処理
- コントローラのアクションメソッド前に実行
- ルーティングごとに紐付け
- ルーティングとアクションメソッド実行の間に挟める処理
- ファイルはクラスで定義
- 管理場所は「app」>「Http」>「Middleware」内
- 作成コマンド:php artisan make:middleware TestMiddleware
Middleware(ミドルウェア)を使用する流れ
- artisanコマンドで作成
- 処理を記述
- ミドルウェアの登録
- ルーティングにミドルウェアを紐づける
1.artisanコマンドで作成
ミドルウェアは専用のスクリプトファイルを作成し処理を記述します。作成はartisanコマンド
で行います。
$ php artisan make:middleware TestMiddleware
Middleware created successfully.
これで「app」>「Http」>「Middleware」内に「TestMiddleware.php(指定したファイル名)」が作成されます。
2.処理を記述(簡素な認証ページの作成)
今回は簡素な認証ページを作ってみます。
認証ページの仕組み
- 「index」ページからPOST送信
- ミドルウェアで値を識別
- 指定値なら「confirm」ページへ
- 指定値以外なら「welcome」ページへ
TestMiddleware.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class TestMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
// リクエストからのPost送信された値を識別 指定値は「token」とする
if ($request->input('token') !== 'token') {
return redirect('/welcome');
}
return $next($request);
}
}
index.blade.php
<body>
<h1>Index</h1>
<form action="confirm" method="post">
@csrf
<input type="text" name="token" value="">
<button type="submit">送信</button>
</form>
</body>
「confirm」ページと「welcome」ページは適当に作成して置いてください。
Laravelではフォーム実装時に@csrf
を設置しないと419ページになるので注意してください。@csrfを無効にしたい場合へ飛ぶ
handleメソッド
作成されたスクリプトファイルの中身は継承もされていないシンプルなクラスです。
中にはhandleメソッド
が1つ用意されておりこの中に自動実行させたい処理を記述していきます。handleメソッド
はRequest
インスタンスを受け取る$request
とレスポンスを返す専用のClosure
(関数)となっている$next
が渡されています。
Request
インスタンスがあることでこれを介してアクセスされたURLやinput
要素の値などを取得できます。
$request->url();
$request->input('token');
Closure
(関数)は$next
の引数にリクエストを渡すことで後続の処置にリクエストをそのまま渡すことができます。
return $next($request);
3.ミドルウェアの登録
作成したミドルウェアは登録しないと使用できません。登録はミドルウェアを管理している「app」>「Http」>「Kernel.php」に追記していきます。$routeMiddleware
配列の中に登録したいミドルウェアのパスを形式に倣って記述します。
Kernel.php
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
// 追記
'test' => \App\Http\Middleware\TestMiddleware::class,
];
4.ルーティングにミドルウェアを紐づける
続いてルーティングとミドルウェアを紐づけます。「web.php」のルーティングからmiddleware
メソッドを呼び出し引数に紐づけたいミドルウェアクラスのパスを渡します。
web.php
Route::post('/confirm', 'App\Http\Controllers\appController@confirm')->middleware(\App\Http\Middleware\TestMiddleware::class);
これでミドルウェアの作成〜登録までの作業は終了です。実際に「indexページ」にアクセスしinput要素
に値を入力して送信してみてください。今回は値が「token」の場合のみ「confirmページ」にアクセスできます。
ミドルウェアとグローバルミドルウェア
ミドルウェアは作成後「web.php」などのルーティングファイルの、定義されたルーティングに1つ1つ紐づけていきました。
しかし例えば全てのアクセスに同じミドルウェアを実行させたい場合は1つ1つ紐づけるより「グローバルミドルウェア」として登録することで全てのルーティングにミドルウェアを紐づけることができます。
グローバルミドルウェアの登録方法
通常のミドルウェアは$routeMiddleware
配列の中に登録したいミドルウェアを記述しましたが、グローバルミドルウェアとして登録する場合は$middleware
配列の中に追記します。
Kernel.php
protected $middleware = [
\App\Http\Middleware\TrustProxies::class,
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
// 追記
\App\Http\Middleware\TestMiddleware::class,
];
先ほどは以下の形式でしたがグローバルミドルウェアの場合はキー値は不要でパスのみ記述すればOKです。
'test' => \App\Http\Middleware\TestMiddleware::class,
↓
\App\Http\Middleware\TestMiddleware::class,
先ほどの簡素な認証ミドルウェアをグローバルにすると全てのページにアクセスできなくなってしまう(全てのページにinput['token']
の値が必要になる)ので注意してください。
ミドルウェアグループの作成
ミドルウェアクラスを複数定義して1つのルーティングに複数紐付けたい場合は「ミドルウェアグループ」を作成すると便利です。「Kernel.php」の$middlewareGroups
には最初から「web」グループと「api」グループがあります。これに倣って新しい配列を作成し中にグループ化したいミドルウェアクラスを記述します。
Kernel.php
protected $middlewareGroups = [
'web' => [
// ちなみに↓この行をコメントアウトすればcsrf機能を無効にできます
\App\Http\Middleware\VerifyCsrfToken::class,
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
],
'api' => [
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
],
'new' => [
// 新しい配列を作成し中にグループ化したいミドルウェアクラスを記述する
\App\Http\Middleware\TestMiddleware::class,
\App\Http\Middleware\Test2Middleware::class,
\App\Http\Middleware\Test3Middleware::class,
],
];
登録が完了したらルーティングからはmiddleware
メソッドの引数に登録したキー値(今回はnew
)を指定するだけです。
web.php
Route::post('/confirm', 'App\Http\Controllers\appController@confirm')->middleware('new');
これでこのルーティングには紐付けたグループに列挙してあるミドルウェアクラスが全て実行されます。
ミドルウェアが動かない時の解決法
ミドルウェアが正しく動作しない場合は以下のポイントをチェックしてみてください。
ミドルウェアが動かない時のポイント
- ミドルウェアクラスを作成していない
- クラス内の処理が正しくない
- 「Kernel.php」に登録していない
- 登録がグローバルやグループになっている
- ルーティングに紐づけていない
ミドルウェアから変数を渡す処理
ミドルウェアからbladeテンプレートに変数としてデータを受け渡すことも可能です。その際は以下の順番で変数を受け渡していきます。
- ミドルウェアクラスにデータを定義
- クラス内で$requestにmerge(結合)
- コントローラ側でmergeされた値を取得
- コントローラ側からbladeファイルへデータを渡す
まずはミドルウェアクラス内に変数とデータを定義→mergeメソッド
を使って$request
にデータを結合します。
TestMiddleware.php
class TestMiddleware
{
public function handle(Request $request, Closure $next)
{
$frameworkList = ['Laravel','CakePHP','Symfony','Zend Framework'];
$data = ['language' => 'php', 'framework' => $frameworkList];
$request->merge(['middleware' => $data]);
return $next($request);
}
}
続いてコントローラ側でリクエストにマージされているデータを設定したキー値middleware
で取得します。それを表示させるbladeテンプレートへ連想配列で渡します。
appController.php
class appController extends Controller
{
public function index(Request $request)
{
// mergeされているデータを取得
$middle = $request->middleware;
// bladeテンプレートへ連想配列で渡す
return view('index', ['middle' => $middle]);
}
public function confirm(Request $request)
{
return view('confirm');
}
}
これでbladeテンプレートからは以下のように渡された変数のデータにアクセスできるようになります。
index.blade.php
<body>
<h2>ミドルウェアからの配列</h2>
<p>{{ $middle['language'] }}</h1>
<ul>
@foreach ($middle['framework'] as $item)
<li>{{$item }}</li>
@endforeach
</ul>
</body>
ミドルウェアからの配列
php
・Laravel
・CakePHP
・Symfony
・Zend Framework
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。