CakePHP4.0.4で標準追加されたBodyParserMiddlewareによってWebAPIアプリケーションが書きやすくなった


そういや表題の通りになったんだけど、Web上に情報がなかったから一筆したためてみたよ!

BodyParserMiddleware とは

CakePHP の Cookbook (リファレンス)には、以下のように書かれています。

cf. ボディパーサミドルウェア | ミドルウェア - CakePHP 4.x Strawberry Cookbook

アプリケーションが JSON、XML、またはその他のエンコードされたリクエストボディを受け入れる場合、 BodyParserMiddleware を使用すると、それらのリクエストを配列にデコードして、 $request->getParsedData() および $request->getData() で利用可能です。 デフォルトでは json ボディのみがパースされますが、オプションでXMLパースを有効にすることができます。 独自のパーサーを定義することもできます。

要は、 Content-Type: application/jsonContent-Type: application/xmlでリクエストされた際のリクエストボディをシュッとパースしてくれるミドルウェアです。

CakePHP 3.6.0class Cake\Http\Middleware\BodyParserMiddleware が追加され、 CakePHPのアプリケーションテンプレートである cakephp/app には 4.0.4 で追加 されました。CakePHP4になってからデフォルトで使用される形になってます。

何が便利?

CakePHP3でWebAPIアプリケーションを書く場合、 Content-Type: application/json でリクエストされた際のリクエストボディを Hash で受け取るには、以下のようにいちいち json_decode() を噛ませる必要がありました。
リクエストボディの一部が欲しくても、一度リクエストボディの全体を受け止めてから部分にアクセスする、みたいなかんじで利便性に欠けていました。

ArticleController.php
<?php
namespace App\Controller;
// 中略
class ArticleController extends AppController
{
    public function edit(string $id)
    {
        $postData = $this->getRequest()->input('json_decode', true);
        $title = $postData['title'];
        // 以下略

仮に Content-Type: application/x-www-form-urlencodedContent-Type: multipart/form-data での POST リクエストだったら $_POST に値が含まれるので、 $this->getRequest()->getData() で取得することはできます。

でも、いまどき application/json とか application/xml でシュッとリクエストしたいじゃないですか〜〜🥺
(まあ、JavaScript / TypeScriptでHTTPクライアントとしてよく用いる axios なんかはデフォルトで Content-Type: application/x-www-form-urlencoded だったりするんですけどね...うーん)

それを解決してくれるのが BodyParserMiddleware です。 $type = 'application/json' のような Content-Type 文字列が定義され、 Content-Type ごとに応じた $parser クロージャを用いて処理してくれます。シュッとしてる!

https://github.com/cakephp/cakephp/blob/4.2.10/src/Http/Middleware/BodyParserMiddleware.php#L165-L170
BodyParserMiddleware.php
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
		// 中略 
        $parser = $this->parsers[$type];
        $result = $parser($request->getBody()->getContents());
        if (!is_array($result)) {
            throw new BadRequestException();
        }
        $request = $request->withParsedBody($result);
        // 以下略

そして、 BodyParserMiddleware で処理されたリクエストボディは、細かい意識をせずに $this->getRequest()->getData() で取得できるようにリクエスト情報に格納してくれます。シュッとしてる!!

ArticleController.php
<?php
namespace App\Controller;
// 中略
class ArticleController extends AppController
{
    public function edit(string $id)
    {
        $title = $this->getRequest()->getData('title'); 
        // 以下略

おわりに

というわけで、WebAPIアプリケーションをCakePHP4で書く場合、よりシュッと書くために動いてくれている BodyParserMiddleware について紹介してみました。
これでキミだけの最強のWebAPIアプリケーションを作ってみてな!!!

参考