LaravelでURLに api/ を付けずにAPIルーティングを実現
結論
-
routes
に専用のルーティング定義ファイルを作成する -
RouteServiceProvider
に新設したルーティング定義ファイルの挙動を定義する
大体これで何とかなるみたいです。
発端
知らないというのは恐ろしいことで。
他サービスからコールさせてシステム連携するためのAPI群を構築するにあたり、設計時点でLaravelの仕様をロクに知らないまま、すべてのエンドポイントの定義を取り決めてしまいました。
当然、そのURLの頭に api/
は付いていません。
APIなので routes/api.php
にルーティングを書けばいいだろうと思ったものの、ここに書いたルーティングには必ずURLの頭に api/
が付くと知り、急遽 routes/web.php
に記述。
しかし、これだとXSRF(CSRF)対策などの理由でCookie周りのHTTPヘッダが勝手に付与されてしまうので、コール元の他サービスの実装によってはこれが悪さをしかねない状態になっており、実際に悪さをしてました。
…やっちまった…。
という事で 【routes/api.php
に既存ルーティングを移設したいんだけど、URLは据え置き】 という要件が発生しました。
調査
※実際にはもう少し迷走してたんですが端折ります
RouteServiceProvider
を確認する
どうやらルーティング定義を読み込むのに使っているのは RouteServiceProvider
というクラスらしいので、app/Providers/RouteServiceProvider.php
を確認。
class RouteServiceProvider extends ServiceProvider
{
// 省略
public function boot(): void
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
$this->routes(function () {
Route::middleware('api')
->prefix('api')
->group(base_path('routes/api.php'));
Route::middleware('web')
->group(base_path('routes/web.php'));
});
}
}
なるほど、routes/api.php
に書いてあるルーティング定義に対して prefix('api')
でURLに api/
が付き、middleware('api')
の処理が行われる。
ということは、API用の定義をコピーして、新しいルーティング定義ファイルを設けたらいいんじゃないか。
新しいルーティング定義ファイルを追加
新しく routes/newapi.php
を設け、今回問題となっている定義を routes/web.php
からこちらに移動しました。
RouteServiceProvider
に定義を追加
app/Providers/RouteServiceProvider.php
に以下を追加しました。
Route::middleware('api')
->group(base_path('routes/newapi.php'));
動作確認
以下の通りキャッシュクリアを実施。
# php artisan cache:clear
# php artisan route:clear
試しにPostmanでAPIを叩いてみたところ、HTTPヘッダからセッション周りのものが消えました。
対応による弊害
これで、api/
を接頭句にしないAPIが実現できるようになりましたが、ひとつ弊害が。
app/Providers/RouteServiceProvider.php
の冒頭にこういう処理があります。
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
これ、「1分間に同一ユーザー or 同一IPからアクセスを受けたら429エラーにする」という仕組みで、おそらくmiddleware('api')
の処理で行っており、routes/web.php
に記述している間はそういった制約はありません。
ここについての話はまた別の記事で。
Discussion