CakePHP 4 で HTTP メソッドの接続可否をルーティングで設定する方法

はじめに

CakePHP 4 のルーティングは、デフォルトで各アクションへの URL がある規則に従って自動的に設定されます。アクションを実装すれば即アクセス可能になるので便利ですが、さらに設定を追記することで任意の URL を割り当てることもできます。

ところで Web 開発ではアクセス可能な HTTP メソッドを限定したい場合があります。
CakePHP 4 ではコントローラでも実装可能ですが、ルーティングで制御することもできます。

今日は CakePHP 4ルーティング設定で、任意のアクションに接続可能なHTTP メソッドを指定する方法をご紹介します

今回使用したのは CakePHP 4.1.5 (cakephp/app 4.1.1) です。

目次
  1. 下準備
  2. 設定方法
  3. 【注意】動作しないケース
  4. おわりに

1. 下準備

今回は SampleController の index アクション を用意し、ルーティング設定でここに接続可能なメソッドを指定します。

/src/Controller/SampleController.php
<?php
declare(strict_types=1);

namespace App\Controller;

class SampleController extends AppController
{
    public function index()
    {
        $this->autoRender = false;
        echo 'OK';
    }
}

動作確認用に下記 req コマンドを用意します。
この req コマンドは各メソッドで sample/index に接続を行い、許可された HTTP メソッドだけを表示するものです。

/src/Command/ReqCommand.php
<?php
declare(strict_types=1);

namespace App\Command;

use Cake\Command\Command;
use Cake\Console\Arguments;
use Cake\Console\ConsoleIo;
use Cake\Console\ConsoleOptionParser;
use Cake\Http\Client;
use Cake\Routing\Route\Route;

class ReqCommand extends Command
{
    public function execute(Arguments $args, ConsoleIo $io)
    {
        // 適宜変更してください
        $url = 'http://localhost/sample';

        // 各メソッドで接続
        $http = new Client();
        foreach (Route::VALID_METHODS as $method) {
            $func = strtolower($method);
            $response = $http->{$func}($url);
            // 成功したメソッドだけ表示
            if ($response->getStatusCode() === 200) {
                $io->out($method);
            }
        }
    }
}

上記コード内の Route::VALID_METHODS には使用可能な HTTP メソッドが格納されていて、/vendor/cakephp/cakephp/src/Routing/Route/Route.php の中で定義されています。

このままだと POST と PUT の際に CSRF トークン不一致エラーになるので、
今回はサンプル動作確認の簡略化のために無効化します。
(通常の開発時は、基本的には有効のままが良いです)

/src/Application.php
$middlewareQueue
    ...
    // 下記をコメントアウトし、最後に ; (セミコロン) を追加
    // ->add(new CsrfProtectionMiddleware([
    //     'httponly' => true,
    // ]));
    ;

2. 設定方法

接続可能な HTTP メソッドの指定には setMethods() を使うことができます。
下記のように routes.php を変更してください。

/config/routes.php
<?php
use Cake\Routing\RouteBuilder;

$routes->scope('/', function (RouteBuilder $builder) {
    $builder->connect('/sample', ['controller' => 'Sample', 'action' => 'index'])
        ->setMethods(['GET', 'PUT']);
});

設定が完了したら、先の「1. 下準備」で作った req コマンドを入力して確認してください。
(接続可能なメソッドだけが表示されます。)

$ cd /path/to/cakephp4
$ bin/cake req
GET
PUT

指定可能な HTTP メソッドは以下の通りです。

  • GET
  • PUT
  • POST
  • PATCH
  • DELETE
  • OPTIONS
  • HEAD

メソッドが一つだけの場合は下記のように書くこともできます。

/config/routes.php
<?php
use Cake\Routing\RouteBuilder;

$routes->scope('/', function (RouteBuilder $builder) {
    // POST メソッドだけを許可
    $builder->post('/sample', ['controller' => 'Sample', 'action' => 'index']);
});

3. 【注意】動作しないケース

例えば下記のように、対象スコープ内で DashedRouteInflectedRoutefallbacks() を実行すると setMethods() が動作しないようです。

/config/routes.php
<?php
// !!! 動作しません !!!
use Cake\Routing\Route\DashedRoute;
use Cake\Routing\RouteBuilder;

$routes->setRouteClass(DashedRoute::class);

$routes->scope('/', function (RouteBuilder $builder) {
    $builder->connect('/sample', ['controller' => 'Sample', 'action' => 'index'])
        ->setMethods(['GET', 'PUT']);

    $builder->fallbacks();
});
# 下記のように全てのメソッドが通ってしまいます
$ bin/cake req
GET
PUT
POST
PATCH
DELETE
OPTIONS
HEAD

この fallbacks() を外せば setMethods() が効くようになりますが、
URL からコントローラとアクションを自動判別しなくなるため、ルーティングの手動設定が必要になります。

fallbacks() については下記公式ドキュメントで紹介されていて、
関数は /vendor/cakephp/cakephp/src/Routing/RouteBuilder.php にあります。

4. おわりに

CakePHP のルーティング設定は、デフォルトで自動的に URL を生成してくれるので、最初はあまり触る機会がなかったりするのですが、細かい設定が可能で奥が深いんですよね。公式ドキュメントの文章量も多いです。

今回ご紹介した setMethods() 等を使えば Laravel でのルーティングのように、意図しないアクセスをはじくことが出来ます。ただし、前述fallbacks() があると動作しない点につきましてはご注意ください。