Laravelで異なるオリジンからAPIを叩く
やりたいこと
Laravelで、異なるオリジンのWebサービスからAPIを叩けるようにしたい。
試してみる
環境
- バックエンド
Laravel 10
http://127.0.0.1:8000で起動
※Laravel7以前と以降で、cors設定の方法が変わっているので注意が必要。 - フロントエンド
next.js 14
http://localhost:3000で起動
検証用のControllerを用意
TestController.php
public function get()
{
return response()->json(['message' => 'hello']);
}
public function post(Request $request)
{
return response()->json(['message' => $request->message]);
}
Next.js側でAPIを叩くボタンを置く
<Button
type="button"
onClick={() => {
axios
.get("http://127.0.0.1:8000/api/test")
.then((res) => {
console.log(res);
})
.catch((error) => {
console.log(error);
});
}}
>
Get
</Button>
<Button
type="button"
onClick={() => {
axios
.post(
"http://127.0.0.1:8000/api/test",
{
message: "test",
},
)
.then((res) => {
console.log(res);
})
.catch((error) => {
console.log(error);
});
}}
>
Post
</Button>
検証① api.phpでルートを用意して叩いてみる。
api.php
Route::get('test', [TestController::class, 'get']);
Route::post('test', [TestController::class, 'post']);
とする。
api.phpは、Middlewareとして、VerifyCsrfToken.phpを通過しないので、
cors.phpで許可されていれば利用可能なはず。
つまり、
VerifyCsrfToken.phpは、
protected $except = [
//
];
のままでOK。
※web.phpに書いたルートを使う場合は、当該ルートをここに記載する必要がある。
そのままGETとPOSTボタンを押してみる。
結果
GETもPOSTもできた。
検証② cors.phpでブロックしてみる。
config/cors.php
を見ると、おそらく、下記のようになっている。
今回使っているpathsは、api/*に該当するし、
allowed_methods,allowed_originsは全部許可になっている。
cors.php
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => false,
なので、
これを下記のように書き換えてみる。
cors.php
'paths' => [''],//←ここを空にした
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => false,
これで、先ほど用意したルートはcorsに該当しないため、弾かれるはず。
変更を反映するために、
php artisan config:cache
を打つ。これをしないと、configの変更が反映されない。
結果
ちゃんと弾かれた。
なお、同様に、
'allowed_origins' => [''],
のように、許可するオリジンを消しても、弾かれた。
検証③ CORSで、originを指定してみる。
cors.phpを、
'allowed_origins' => ['http://localhost:3000'],
にして、http://localhost:3000のみで、CORSを許可してみる。
変更したら、忘れず、php artisan config:cache。
結果
GETも、POSTもできる。
ちなみに、
'allowed_origins' => ['http://localhost:3000/'],
のように、最後に/を加えると、ブロックされるので注意。
検証④ allowed_methodsを指定してみる。
cors.phpを、
'allowed_methods' => ['GET'],
にしてみる。
これだと、GETしかできなくなるのでは?
php artisan config:cacheをしてからGETとPOSTをしてみる。
結果
GETも、POSTもできる。
あれ?
検証⑤ PUTとDELETEもやってみる。
next.js側で、GET,POSTに加えて、PUTとDELETEも用意。
<Button
type="button"
onClick={() => {
axios
.put(
"http://127.0.0.1:8000/api/test",
{
message: "put-test",
}
)
.then((res) => {
console.log(res);
})
.catch((error) => {
console.log(error);
});
}}
>
Put
</Button>
<Button
type="button"
onClick={() => {
axios
.delete(
"http://127.0.0.1:8000/api/test"
)
.then((res) => {
console.log(res);
})
.catch((error) => {
console.log(error);
});
}}
>
Delete
</Button>
laravel側でも、
api.php
Route::get('test', [TestController::class, 'get']);
Route::post('test', [TestController::class, 'post']);
Route::put('test', [TestController::class, 'put']);
Route::delete('test', [TestController::class, 'destroy']);
TestController.php
public function get()
{
return response()->json([
'message' => 'Hello',
]);
}
public function post(Request $request)
{
return response()->json([
'message' => 'post:'.$request->message,
]);
}
public function put(Request $request)
{
return response()->json([
'message' => 'put:'.$request->message,
]);
}
public function destroy()
{
return response()->json([
'message' => 'Deleted',
]);
}
として、PUTとDELETEも送ってみる。
で、
cors.phpは、
'allowed_methods' => ['GET'],
で、GETのみ許可。
php artisan config:cacheをしてからGET,POST,PUT,DELETEをしてみる。
結果
GET→〇
POST→〇
PUT→✕
DELETE→✕
検証⑥ PUTを許可してみる。
cors.phpで、
'allowed_methods' => ['PUT'],
にして、
php artisan config:cacheをしてからGET,POST,PUT,DELETEをしてみる。
結果
GET→〇
POST→〇
PUT→〇
DELETE→✕
検証⑦ DELETEを許可してみる。
cors.phpで、
'allowed_methods' => ['DELETE'],
にして、
php artisan config:cacheをしてからGET,POST,PUT,DELETEをしてみる。
結果
GET→〇
POST→〇
PUT→✕
DELETE→〇
つまり、GETとPOSTは常に許可されているっぽい。
GETやPOSTはダメだけど、PUTやDELETEならOKって場面はあんまりなさそうだから、まぁいいのかな。
まとめ
- cors.phpを編集したときは、反映させるのに、php artisan config:cacheが必要。
- methodは、originが許可されていれば、GETとPOSTは常に許可されてるっぽい。
おまけ
Cookieを含むリクエストを送る場合は、
cors.php
'supports_credentials' => true,
にして、
next.js側で、
axiosのオプションに、
{ withCredentials: true }
が必要なよう。
これは、LaravelSanctumによるSPA認証を行うなら、必要になってくる。
おそらく、Sanctumを使うなら、上記だけでは設定は不足すると思われるが、
本記事ではそこまで触れないので、
公式ドキュメントを参照。
注意が必要なのは、Sanctumを使わないにしても、
cors.php
'supports_credentials' => false,
なのに、
axiosのオプションに、
{ withCredentials: true }
にして、フロントエンドからアクセスすると、pathやoriginで許可していても弾かれる。
Discussion