2021年10月22日

Fetch

JavaScript は、必要に応じていつでもサーバへリクエストを送信し、新しい情報を読み込むことができます。

例えば、次のようなことができます:

  • 注文を送信する
  • ユーザ情報を読み込む
  • サーバから最新の更新情報を受け取る
  • …など

…そしてこれらはすべてページをリロードすることなく行うことができます。

それを表す包括的な用語 “AJAX” (Asynchronous Javascript And Xml)があります。XML を使う必要はありません: この用語は昔から来ています。

ネットワークリクエストを送信し、サーバから情報を取得するための様々な方法があります。

fetch() メソッドはモダンで多目的に利用できるため、これから始めましょう。fetch は数年間進化と改善を続けています。今のところサポートはブラウザの間でとてもしっかりしています。

基本構文は次の通りです:

let promise = fetch(url, [options])
  • url – アクセスする URL
  • options – オプションのパラメータ: メソッドやヘッダなど

ブラウザはすぐにリクエストを開始し、promise を返します。

レスポンスの取得は通常2段階のプロセスになります。

promise は、サーバがヘッダを応答するとすぐに組み込みの Response クラスのオブジェクトで resolve します。

そのため、HTTP ステータスをチェックすることで、リクエストが成功したのか失敗したのかを確認したり、ヘッダをチェックすることができます。が、まだ本文は持っていません。

promise は fetch が HTTP リクエストを作るすることができなかった場合、例えば ネットワークの問題やそのようなサイトがない場合に reject します。404 や 500 のような HTTP エラーも通常のフローとみなされます。

レスポンスのプロパティでそれらを確認することができます。:

  • ok – boolean, HTTP ステータスコードが 200-299 の場合 true です。
  • status – HTTP ステータスコード.

例:

let response = await fetch(url);

if (response.ok) { // HTTP ステータスが 200-299 の場合
  // レスポンスの本文を取得(後述)
  let json = await response.json();
} else {
  alert("HTTP-Error: " + response.status);
}

レスポンスの本文を取得するには、追加のメソッド呼び出しが必要です。

Response は様々な形式で本文にアクセスするための、複数の promise ベースのメソッドを提供しています。:

  • response.json() – レスポンスを JSON オブジェクトとしてパースします
  • response.text() – レスポンスをテキストとして返します
  • response.formData() – FormData オブジェクト(form/multipart encoding) として返します,
  • response.blob()Blob (型付きのバイナリデータ) としてレスポンスを返します,
  • response.arrayBuffer()ArrayBuffer (純粋なバイナリデータ) としてレスポンスを返します,
  • 加えて, response.bodyReadableStream オブジェクトで、本文をチャンク毎に読むことができます。後ほど例を見ていきましょう。

例えば、ここでは Github から最新のコミットの JSON オブジェクトを取得します。:

let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits');

let commits = await response.json(); // レスポンスの本文を読み JSON としてパースする

alert(commits[0].author.login);

もしくは、純粋な promise 構文を使用した場合は次のようになります:

fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits')
  .then(response => response.json())
  .then(commits => alert(commits[0].author.login));

テキストを取得するには:

let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits');

let text = await response.text(); // レスポンスボディをテキストとして読む

alert(text.slice(0, 80) + '...');

また、バイナリの例では、画像を取得して表示してみましょう(blob に対する操作の詳細については、チャプター Blob を参照してください)。:

let response = await fetch('/article/fetch/logo-fetch.svg');

let blob = await response.blob(); // Blob オブジェクトとしてダウンロード

// <img> を作成
let img = document.createElement('img');
img.style = 'position:fixed;top:10px;left:10px;width:100px';
document.body.append(img);

// 表示
img.src = URL.createObjectURL(blob);

setTimeout(() => { // 3秒後に隠す
  img.remove();
  URL.revokeObjectURL(img.src);
}, 3000);
重要:

本文のパース方法は1つだけ選ぶことができます。

もし response.text() でレスポンスを取得した後、response.json() は動作しません。本文のコンテンツは既に処理されているためです。

let text = await response.text(); // 本文を返す
let parsed = await response.json(); // 失敗 (既に本文は処理済み)

Headers

response.headers には、Mapライクなヘッダオブジェクトがあります。

個々のヘッダを取得したり、それらをイテレートすることができます。

let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits');

// ヘッダを1つ取得
alert(response.headers.get('Content-Type')); // application/json; charset=utf-8

// すべてのヘッダをイテレート
for (let [key, value] of response.headers) {
  alert(`${key} = ${value}`);
}

ヘッダを設定するには、次のように headers オプションを使用します:

let response = fetch(protectedUrl, {
  headers: {
    Authentication: 'abcdef'
  }
});

…しかし、設定できない 禁止された HTTP ヘッダ のリストがあります。:

  • Accept-Charset, Accept-Encoding
  • Access-Control-Request-Headers
  • Access-Control-Request-Method
  • Connection
  • Content-Length
  • Cookie, Cookie2
  • Date
  • DNT
  • Expect
  • Host
  • Keep-Alive
  • Origin
  • Referer
  • TE
  • Trailer