fetch()は404を通信失敗として扱わずresponseのstatusを確認する
公開日: 執筆:佐藤
JavaScriptのfetch()は、serverから404や500のHTTP responseが返ったことだけでは失敗の処理へ進みません。responseを受け取ったあとで、statusやokを確認します。通信そのものが完了しなかった場合、serverがエラーを返した場合、本文を読めなかった場合を一つの「取得失敗」にまとめないことが大切です。
画面に商品一覧、予約可能な日時、問い合わせの送信結果を出すとき、利用者が必要なのはJavaScriptの例外名ではありません。何をしようとして失敗したのか、もう一度試せるのか、別の連絡方法があるのかを分かる形で出します。codeの分岐は、この案内と担当者が調べる記録を作るために置きます。
HTTP responseを受けたことと正常な結果を分ける
Fetch Standardは、requestとresponseを扱う仕組みを定めています。fetch()で得るResponseには、HTTP statusやheader、bodyがあります。404 Not Foundや500 Internal Server Errorは、serverがHTTP responseを返した状態です。URLや処理に問題があっても、responseを確認できるなら、statusと本文を読んで次の処理を決められます。
response.okは、statusが200から299の範囲かを確かめるために使えます。ただし、okがfalseなら原因まで分かるわけではありません。404ならURLが存在しないか、公開範囲が違うかを調べます。401や403なら、login状態や権限、送る認証情報を確認します。500なら、requestを受けたserver側の処理と記録を確認します。同じfalseでも、直す場所と読者へ出す案内は違います。
説明用の仮例として、予約枠を表示するpageが/api/slotsからJSONを取得するとします。404なら、URLを変更したのにJavaScriptだけが古いURLを呼んでいるかもしれません。500なら、予約枠を作る処理やdatabase接続に問題があるかもしれません。どちらも一覧が表示されない結果は同じでも、「時間を置いて再読み込みしてください」と出すだけでは担当者が調べる手掛かりを失います。status、requestしたURL、発生時刻を記録して分けます。
通信ができない場合はHTTP statusを前提にしない
通信が途中で止まった、名前解決ができない、接続が遮断されたような場合は、HTTP responseを受け取れません。statusが404か500かを確認する処理へ進めないため、通信できた場合とは別に扱います。画面では、serverの障害と決めつけず、通信できなかったこと、再試行の方法、必要なら別の連絡先を示します。
ここでありがちな誤りは、すべての例外を「dataがありません」と表示することです。商品がないのか、利用者の接続が切れたのか、serverが異常なのかが同じ画面になると、読者は何をすればよいか分かりません。商品が見つからない場合にはカテゴリへ戻るlinkを出し、通信できない場合には再読み込みを案内し、送信を伴うformなら二重送信を避けるために送信内容の扱いを分けます。
問い合わせformでは、送信buttonを押したあとに通信できないとき、送信が受け付けられなかったのか、受け付けたが画面へ返答できなかったのかを利用者だけで区別できない場合があります。送信済みと表示して終わらせず、受付を記録する仕組み、重複時にどう扱うか、電話やメールの案内を先に決めます。JavaScriptで失敗を拾うことは、問い合わせを受けた証明の代わりにはなりません。
本文の形式もstatusとは別に確認する
responseが200でも、期待したJSONが返るとは限りません。login画面のHTML、serverのerror page、空の本文などをJSONとして読むと、画面のJavaScriptは別の場所で止まります。HTTP statusだけで正常と決めず、必要なcontent typeと本文の内容を確かめます。
たとえば、一覧をJSONで受け取る設計なら、正常なresponseであることを確認したあと、JSONとして読み、必要な項目があるかを確かめます。商品の名前と価格を出すなら、配列があることだけでなく、各項目に名前と価格があるかを確認します。欠けたdataを空文字で表示すると、利用者は商品名も価格もない行を見ます。取得したdataが何を意味するかは、通信処理だけでは決まりません。
エラーresponseの本文にも、server側が利用者へ伝える理由が含まれる場合があります。けれども、その本文をそのまま画面へ表示しません。内部のfile path、databaseの情報、実装の詳細が混じれば、必要以上の情報を公開します。画面に出す文は、利用者が次にできる行動を短く示し、詳細なstatusと本文は調査用の記録へ残すように分けます。
確認は成功の画面だけで終わらせない
実装後には、200で正しいdataを返す場合だけでなく、404、403、500、通信できない場合を順に試します。開発用のURLでstatusを変えられないなら、存在しないURLを呼ぶ、loginしていない状態で権限が必要な操作を行う、server側で意図的にエラーresponseを返すなど、検証用の条件を用意します。本番の利用者に試させません。
確認する項目は四つです。第一に、画面が読める案内を出し、操作できないbuttonを残していないかを見ます。第二に、成功したときだけ表示すべきdataが、失敗時に古いまま残っていないかを見ます。第三に、開発者がstatus、URL、時刻、利用者の操作を追える記録があるかを見ます。第四に、送信を伴う処理で、再読み込みや再試行によって同じ注文や問い合わせが重ならないかを見ます。
外部APIを使う場合は、相手のserviceが返すstatusと本文の形式を、契約や文書で確認します。自社の画面で使う言葉を外部APIのmessageへ任せると、相手側の変更で案内が変わります。dataの取得を委託しても、読者へ何を表示し、失敗時に誰が確認するかはsite側の判断です。
最初の一手は、fetch()を使う箇所を一つ選び、成功、HTTP error、通信失敗、本文の不正という四つの結果を表にすることです。それぞれに、画面へ出す案内、残す記録、再試行の扱いを決めます。statusを見るcodeを増やす前に、失敗したときも読者と担当者が次の行動を選べる状態を作ります。
関連記事

