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