【PHPフレームワークFlow】FlowでPSR-15のカスタムミドルウェアを扱う

# 初めに
PHPにおけるリクエストとレスポンスを扱うためのガイドラインであるPSR-15は主にFW内で使われる技術であり、Controller以降を実装する開発者にとって意識する機会は少ないかもしれません。
そんな中、Flowでは独自のミドルウェアを作成し、ミドルウェアチェインの中に組み込む機能を有しています。

今回は、Flowにおけるカスタムミドルウェアの作成方法とその組み込み方について解説します。

# PSR-15とは
PSR-15は、Web APIやWebアプリケーションのリクエストとレスポンス処理を共通化・標準化するためのガイドラインです。

https://www.php-fig.org/psr/psr-15/

詳しくは以下の記事で解説をしたので、ご覧ください。

https://qiita.com/nnhkrnk/items/912fa9ade4352bf1c764

# FlowにおけるPSR-15
前述の通り、FlowではPSR-15のルールに沿ってリクエストとレスポンスを扱う処理を共通化しています。そして、Flowでは開発者が独自に作成したミドルウェアをミドルウェアチェインの中に組み込むことができるのです。

# ミドルウェアを追加してみる
ということで、さっそく追加してみましょう。
今回は『特定のページへのアクセスを禁止する』ための処理をミドルウェアを用いて作成してみます。

初めに、Flowにおけるデフォルトのミドルウェア一覧を見てみましょう。
Flowにはミドルウェアの一覧を確認する`./flow middleware:list`というコマンドが用意されています。

```sh
> ./flow middleware:list
Currently configured middlewares:
+----+---------------------+---------------------------------------------------------+
| #  | Name                | Class name                                              |
+----+---------------------+---------------------------------------------------------+
| 1  | standardsCompliance | Neos\Flow\Http\Middleware\StandardsComplianceMiddleware |
| 2  | trustedProxies      | Neos\Flow\Http\Middleware\TrustedProxiesMiddleware      |
| 3  | methodOverride      | Neos\Flow\Http\Middleware\MethodOverrideMiddleware      |
| 4  | session             | Neos\Flow\Http\Middleware\SessionMiddleware             |
| 5  | ajaxWidget          | Neos\FluidAdaptor\Core\Widget\AjaxWidgetMiddleware      |
| 6  | routing             | Neos\Flow\Mvc\Routing\RoutingMiddleware                 |
| 7  | poweredByHeader     | Neos\Flow\Http\Middleware\PoweredByMiddleware           |
| 8  | flashMessages       | Neos\Flow\Mvc\FlashMessage\FlashMessageMiddleware       |
| 9  | parseBody           | Neos\Flow\Http\Middleware\RequestBodyParsingMiddleware  |
| 10 | securityEntryPoint  | Neos\Flow\Http\Middleware\SecurityEntryPointMiddleware  |
| 11 | dispatch            | Neos\Flow\Mvc\DispatchMiddleware                        |
+----+---------------------+---------------------------------------------------------+
```

## 手順1：ミドルウェアクラスを作成
ということで、ミドルウェアを作成しました。
PSRの`MiddlewareInterface`を継承して作成します。

```php: AccessCheckMiddleware
<?php
namespace Neos\Welcome\Middleware;

use Neos\Flow\Mvc\ActionResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

class AccessCheckMiddleware implements MiddlewareInterface
{

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        if ($request->getUri()->getPath() === '/Neos.Welcome/wip') {
            $actionResponse = new ActionResponse();
            $actionResponse->setStatusCode(500);
            $actionResponse->setContent('ここはまだ作成中です！');
            return $actionResponse->buildHttpResponse();
        }
        return $handler->handle($request);
    }
}
```

## 手順2：Settings.yamlに追加したミドルウェアを定義
続いて、作成したミドルウェアをミドルウェアチェインに組み込んでいきます。
Settings.yamlに以下の記載をしました。
どこに追加するかも記載します。今回は`dispatch`の前に追加をしました。

```yaml
Neos:
  Flow:
    http:
      middlewares:
        'AccessCheckMiddleware':
          position: 'before dispatch'
          middleware: 'Neos\Welcome\Middleware\AccessCheckMiddleware'
```

この2ステップでミドルウェアの追加は完了です。

`./flow middleware:list`を実行したところ、ミドルウェアチェインの11番目に組み込まれていることが確認できました。

```sh
> ./flow middleware:list
Currently configured middlewares:
+----+-----------------------+---------------------------------------------------------+
| #  | Name                  | Class name                                              |
+----+-----------------------+---------------------------------------------------------+
| 1  | standardsCompliance   | Neos\Flow\Http\Middleware\StandardsComplianceMiddleware |
| 2  | trustedProxies        | Neos\Flow\Http\Middleware\TrustedProxiesMiddleware      |
| 3  | methodOverride        | Neos\Flow\Http\Middleware\MethodOverrideMiddleware      |
| 4  | session               | Neos\Flow\Http\Middleware\SessionMiddleware             |
| 5  | ajaxWidget            | Neos\FluidAdaptor\Core\Widget\AjaxWidgetMiddleware      |
| 6  | routing               | Neos\Flow\Mvc\Routing\RoutingMiddleware                 |
| 7  | poweredByHeader       | Neos\Flow\Http\Middleware\PoweredByMiddleware           |
| 8  | flashMessages         | Neos\Flow\Mvc\FlashMessage\FlashMessageMiddleware       |
| 9  | parseBody             | Neos\Flow\Http\Middleware\RequestBodyParsingMiddleware  |
| 10 | securityEntryPoint    | Neos\Flow\Http\Middleware\SecurityEntryPointMiddleware  |
| 11 | AccessCheckMiddleware | Neos\Welcome\Middleware\AccessCheckMiddleware           |
| 12 | dispatch              | Neos\Flow\Mvc\DispatchMiddleware                        |
+----+-----------------------+---------------------------------------------------------+
```

実際にAPIを呼び出したところ、早期リターンしていることが確認できました！

```sh
>curl -i -X GET http://localhost:8081/Neos.Welcome/wip
HTTP/1.1 500 Internal Server Error
Host: localhost:8081
Date: Sun, 18 Aug 2024 02:33:50 GMT
Connection: close
X-Powered-By: PHP/8.2.17
X-Flow-Powered: Flow/8.3
Content-Length: 33
Content-type: text/html; charset=UTF-8

ここはまだ作成中です！
```

いい感じに動いてますね！:slight_smile:


# まとめ
カスタムミドルウェアは以下の手順で作成する
1. PSRの`MiddlewareInterface`を継承したミドルウェアを定義する
1. Settings.yamlに作成したミドルウェアを定義し、ミドルウェアチェインに加える

# 終わりに
今回はFlowにおけるカスタムミドルウェアの実装方法を解説しました。
簡単にできるので、全体的な処理を書く際はここに組み込むのもいいかもしれません。

ここまでご覧いただきありがとうございました！