Fetch API を使った時に redirect が発生すると default では response.ok = true になるので注意
TL;DR
- JavaScript の Fetch API を使った時に redirect が発生すると default では Response.ok = true になる(こともある)
- それどころか Response.status もリクエスト先が302だろうが200になったりする
- これは、Fetch API は redirect オプションでリクエスト先からリダイレクトステータスが返ってきた時の挙動を指定することができ、その default 挙動が「リダイレクトに従う」だから
- 最終的なリダイレクト先が HTTP 404 とかなら reponse.ok = false になりそうではある(あんまりなさそう)
- Fetch API | MDN
- redirect オプションを指定せずデフォルト挙動のまま、
Response.redirected
を用いて判別してもいいし、Fetch API 実行時にredirect: 'error'
やredirect: 'manual'
を指定することで制御してもいい
redirect が発生した時の redirect オプションごとの Response オブジェクト
まず、fetchAPIはこんな感じで実行するとする
const response = await fetch("/hoge/fuga", {
method: "POST",
credentials: "same-origin",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(postBody),
redirect: "follow"
});
※fetchAPI実行時に redirect
オプションを省略すると redirect: 'follow'
扱いで実行される
redirect: 'follow'
Response: {
body: ..., // リクエストによる
bodyUsed: false,
headers: ..., // リクエストによる
ok: true,
redirected: true, // ここが true!!
status: 200, // 最終的なリダイレクト先の HTTP status!!
statusText: "OK", // 最終的なリダイレクト先の HTTP status Text!!
type: "basic",
url: "https://<最終的なリダイレクト先のURL>" // 最終的なリダイレクト先のURL!!
}
Response.ok 読取専用
レスポンスが成功 (200–299 の範囲のステータス) したか否かを通知する論理値が入ります。
と MDN にはあるが、fetch API でのリクエスト先が HTTP 302 などでリダイレクト指定がある場合は
リダイレクトが発生した後の「その先のURL」の HTTP Status が入ってくる。
リダイレクト先からさらにリダイレクトした場合、最終的なリダイレクト先のURLへリクエストした結果がresponseオブジェクトに入ってくるようだ。
最終的なリダイレクト先が HTTP 404 とかなら reponse.ok = false になりそうではある(あんまりなさそう)
redirect: 'error'
fetch API (= fetch()
)自体が例外を吐くので
Response オブジェクトは取得できない
catch すると net::ERR_FAILED 302 (Found)
redirect: 'manual'
Response: {
body: ..., // リクエストによる
bodyUsed: false,
headers: ..., // リクエストによる
ok: false,
redirected: false,
status: 0, // マジで0
statusText: "", // マジで空文字
type: "opaqueredirect",
url: "https://<fetch API のリクエスト先>"
}
"manual" – HTTP リダイレクトには従いませんが、response.url が新しい URL になり、response.redirected が true になります。必要に応じて新しい URL へ手動でリダイレクトすることができます。
と JavaScript.info にはあるが、Google Chrome v124.0.6367.119 で試した感じ
redirected: false
だった。
また、
Response.redirected 読取専用
レスポンスがリダイレクトの結果である (つまり、その URL リストには複数のエントリーがある) かどうかを示します。
と MDN にはあるが、 Google Chrome v124.0.6367.119 で試した感じ
Response.url
は単一の String だしどこに URL リストがあるのかはわからなかった。
しかも Response.url
に元々の fetch API のリクエスト先があるので
redirect: 'manual'
と言いつつ、リダイレクト先を知り再度fetchしなおす方法すらないように感じる。
Response.headers.get('Location')
してもリダイレクト先は取得できなかった。
な、何だこれは……
と思ったらこんなISSUEがあった
Cannot get next URL for redirect="manual"
That is by design
redirect: 'manual'
がリダイレクト先を知れないのは仕様だそうだ。無念…
気になるところをまとめると
Response.ok |
Response.redirected |
Response.url |
|
---|---|---|---|
redirect: 'follow' |
true ※ |
true |
最終的なリダイレクト先のURL |
redirect: 'error' |
(例外になる) | - | - |
redirect: 'manual' |
false |
false |
fetch API のリクエスト先 |
※最終的なリダイレクト先が HTTP 404 とかなら reponse.ok = false になりそうではある(あんまりなさそう)
結局、リダイレクトをどう制御してやるのがいいか
まずはこの記事の主題である
Fetch API を使った時に redirect が発生すると default では response.ok = true になる
をとにかく知っておく必要がある。
その上で、リクエスト先がリダイレクトが発生し得ないのであれば気にしなくていい。
ただし、POST時の認証セッション切れなどでリダイレクトが発生し得るのであれば、以下の制御を行っておいた方が良さげ。
-
redirect: 'follow'
(または省略) を使う-
Response.redirected
が唯一 true になり得るのがこれ -
Response.redirected
が true だったらリダイレクトが発生してるので、リダイレクトした時の制御をしてやればいい -
Response.url
には最終的なリダイレクト先のURLが格納されているので、それを使って 再度fetchしその結果のResponse.text()
を使って強引にリダイレクト先のDOMを使ってnew DOMParser().parseFromString(Response.text(), "text/html")
みたいなことをして強引にリダイレクト先のDOMを使ってアレコレすることもできそうだが、あんまりそういう変な?ことはしない方がいい……と思う……- 詳しく知りたい人がもしいればコメントください
-
-
redirect: 'error'
を使う- リダイレクトが発生したら例外になるので try-catch してやればいい
- fetchAPI の error は基本的に通信での失敗のケースのみで発生するはずなので、catch時の制御は通信の失敗 or fetchAPI実行時のリダイレクト発生となるので、そこだけは注意
-
redirect: 'manual'
を使う- マニュアルとか言いつつ、実態は「エラーにはしないが、リダイレクト先に追従もしない」って挙動
- ただ、
redirect: 'follow'
とは違いリダイレクトが発生するとResponse.ok
が false になるのでif (!Response.ok)
みたいな条件で HTTP 200番台以外のステータスコード時の制御と一律で制御組めるのはよさそう
もっと簡単にまとめて!
- エラーにするなら
redirect: 'error'
-
Response.ok
で制御するならredirect: 'manual'
-
Response.redirected
で制御するならredirect: 'follow'
(or指定なし)
が結論ってことでどうでしょうか
参考
- fetch APIのredirect関連メモ | 銀色うつ時間
- Fetch API fetch() の使い方 | Web Design Leaves
Discussion