#071
posted on 2022.05.27 (Fri) 2023.01.14 (Sat)

FetchとXMLHttpRequestの基礎知識。

JavaScriptでクライアントサイドからサーバーサイドへ非同期にHTTP通信を実行して別ベージのコンテンツやリソースの取得ができるFetchとXMLHttpRequestについての基礎知識のメモ。

 

 

FetchとXMLHttpRequestの違い

どちらもJavaScriptで非同期にHTTP通信が実行できる「Fetch」と「XMLHttpRequest」の違い。

  • 「Fetch」の方が「XMLHttpRequest」よりも新しく、機能が優れるので、現在は「Fetch」の方が推奨されている。
  • ほとんどの「XMLHttpRequest」の機能がIE対応だが、「Fetch」は完全IE非対応。
  • 「XMLHttpRequest」はアップロードとダウンロードの両方の進行状況を追跡できるが、「Fetch」はダウンロードのみでアップロードの進行状況の追跡はできない。(仕様が更新される可能性あり。)
  • 別ページの「HTML」を取得したい場合、「XMLHttpRequest」はリソースを直接「XML」や「HTML」としてparse(解釈)して取得できるが、「Fetch」では一旦「文字列」としてデータを取得してから「DOMParser」インターフェイスでparse(解釈)し直す必要がある。

 

 

Fetch

(1) Fetch

「Fetch API」は、指定したパスにリクエストを送り、そのレスポンスとしてリソースを取得するための機能を提供する。(HTTP通信のリクエスト送信とレスポンス受信をJavaScritpで処理するために利用される。)

「fetch()」メソッドを使用して非同期通信を実行し、主に、別ページのコンテンツやサーバー上のリソースの取得などに利用される。

※ Fetch APIについてのMozillaの公式ドキュメント

 

(2) Fetchの要約

  • 「Fetch」はIE非対応。
  • 「Fetch API」では、HTTP通信の「ヘッダー」、「リクエスト」、「レスポンス」を扱うためのインターフェイスが定義されている。
  • 「Headers」オブジェクト、「Request」オブジェクト、「Response」オブジェクトを内部的に処理してHTTP通信を実行する。
  • 「Headers」オブジェクトは、HTTP通信のリクエストとレスポンスの「ヘッダー」を表す。
  • 「Request」オブジェクトは、取得したいリソースへの「リクエスト」の内容を表す。
  • 「Response」オブジェクトは、リクエストに対する「レスポンス」の内容を表す。
  • 「fetch()」メソッドを使用して、「取得したいリソースのパス」と「処理に関するオプション」を引数に指定してリクエストを作成・送信し、そのレスポンスを受信する。(「fetch()」メソッドでのリクエストに対するレスポンスに解決するPromiseを返す。)
  • 「fetch()」メソッドはPromiseベースなのでPromiseのメソッドが使用できる。(Promiseのすべてのメソッドが使えるのかは不明。)
  • 「fetch()」メソッドのPromiseは、リクエスト先が応答した時(完全なレスポンスがダウンロードされる前)にresolve(解決)され、「then()」メソッドでレスポンス(Responseオブジェクト)を受け取る。
  • 「Response」オブジェクトを受け取った段階で、完全なレンスポンスを取得するための「Response」オブジェクトのメソッドが利用できる。
  • 「Response」オブジェクトのメソッドで完全なレスポンスを取得することで、レスポンス内のコンテンツ本体が利用できる。(このメソッドは、完全なレスポンスの取得で解決されるPromiseオブジェクトを返す。)
  • クロスオリジン制約により、Cookieやユーザーの資格情報 (HTTPのBasic認証など)は「リクエスト先のURL」が「呼び出し元のスクリプト」と同一オリジンだった場合にだけリクエストに含まれて送信される。(メソッドのオプション指定で、クロスオリジン対応や非送信にも変更できる。)

 

オリジン

オリジンは、ウェブコンテンツへのアクセスに使われる「URLのスキーム」(プロトコル)、「ホスト」(ドメイン)、「ポート番号」の3つによって定義される。

リクエスト元とリクエスト先のオリジンが同じ場合は「同一オリジン」、オリジンの定義のひとつでも異なる場合は「クロスオリジン」となる。

操作する内容によっては同一オリジンでのみ許可されているが、リモート側が特別なヘッダー情報を送信することでクロスオリジンでも許可することができる。このヘッダー情報によって、クロスオリジンリクエストでもフロントエンドのJavaScriptコードがアクセスすることを許可するシステムを「CORS」(Cross-Origin Resource Sharing)と呼ぶ。

 

(3) Headersオブジェクト

「Headers」オブジェクトは、「Fetch」でのHTTP通信のリクエストとレスポンスのヘッダー情報を表す。

※ HeadersについてのMozillaの公式ドキュメント

  • 「Headers」オブジェクトのメソッドを利用して、ヘッダー情報の取得、設定、追加、削除ができる。
  • 新しい「Headers」オブジェクトを生成するには「Headers()」コンストラクターを使用する。
  • 「Headers」オブジェクトは、0個以上の「名前」と「値」のペアで構成されるヘッダーのリストを保持している。
  • 一部のヘッダー情報は設定が禁止されている。(「forbidden header name」の公式ドキュメント。)
  • クロスオリジンリクエストの場合、デフォルトでは単純レスポンスヘッダーの情報しか取得できない。(サーバーが「Access-Control-Expose-Headers」で許可する必要がある。)
  • 「Request」オブジェクト、「Response」オブジェクトの「headers」プロパティーで、それぞれの「Headers」オブジェクトを参照できる。

 

[ Headersオブジェクトを生成する例 ]

「Headers()」コンストラクターを使用して「Headers」オブジェクトを新しく生成する例。

※ Headers()コンストラクターについてのMozillaの公式ドキュメント

  1. new演算子を付加して「Headers()」コンストラクターを呼び出し、「Headers」オブジェクトを生成。
  2. 「append()」メソッドでヘッダー情報を追加。
  3. 「get()」メソッドでヘッダー情報を取得。

※ 「Headers()」コンストラクターの引数に、「オブジェクトリテラル」か「配列リテラル」でヘッダー情報を指定して、生成と同時に設定することもできる。

//Headersオブジェクトを生成
const ag2headers = new Headers();
//ヘッダー情報を追加
ag2headers.append('Content-Type', 'text/xml');
//名前を指定してヘッダー情報を取得
ag2headers.get('Content-Type');

//Headersオブジェクトの生成時にヘッダー情報を設定する場合
const ag2headers = new Headers({
  'Content-Type': 'text/xml'
});

 

(4) Requestオブジェクト

「Request」オブジェクトは、「Fetch」でのリクエスト送信の内容を表す。

※ RequestについてのMozillaの公式ドキュメント

  • 「Request」オブジェクトは、「fetch()」メソッドの処理の中でリソースへのリクエストとして送信される。
  • 「Request」オブジェクトのプロパティー(「method」や「body」など)を参照して、リクエストに設定されている内容を取得できる。
  • 新しい「Request」オブジェクトを生成するには「Request()」コンストラクターを使用する。
  • 「Request()」コンストラクターの引数にオプションを指定することで、リクエストの内容を任意に設定できる。

 

「Fetch」でリクエストに本文(body)を追加する場合に使用できるデータの形式は以下のいずれか。

  • 文字列。
  • JSONデータ。(任意のオブジェクトをJSONデータとして送信する場合は、「JSON.stringify()」メソッドでJSON化する。)
  • バイナリーデータ : ArrayBufferオブジェクト、ArrayBufferのViewオブジェクト(Uint8Arrayなど)、Blobオブジェクト(またはFileオブジェクト)など。
  • フォーム : FormDataオブジェクト。(HTMLのform要素に該当。)
  • 検索パラメータ : URLSearchParamsオブジェクト。

※ リクエスト通信がGETメソッドまたはHEADメソッドの場合は本文を持てない。

※ JSONなら「application/json」など、送信するデータのタイプに合わせてリクエストヘッダーの「Content-Type」を設定する必要がある。(データとなるオブジェクトが既にタイプ情報を保持していれば設定不要。)

※ 「FormData」オブジェクトをJSON化するには、一度「Object.fromEntries()」メソッドで型を変換してから「JSON.stringify()」メソッドで変換する。

 

[ Requestオブジェクトを生成する例 ]

「Request()」コンストラクターを使用して「Request」オブジェクトを新しく生成する例。

※ Request()コンストラクターについてのMozillaの公式ドキュメント

  1. new演算子を付加して「Request()」コンストラクターを呼び出し、「Request」オブジェクトを生成。
  2. 第1引数に「取得したいリソースのパス」(リクエスト送信先)を指定。
  3. 第2引数に「リクエストに適用するカスタム設定を含むオプションオブジェクト」を指定。(省略可。)

※ オプションの「headers」の値には、「Headers」オブジェクトを指定することもできる。

const ag2request = new Request('取得するリソースのパス', {
  //オプション指定
  method: 'POST',
  headers: {
    'Content-Type': 'text/xml'
  },
  body: '送信するデータ',
  mode: 'cros'
});

 

(5) Responseオブジェクト

「Response」オブジェクトは、「Fetch」でのリクエストに対するレスポンスの内容を表す。

※ ResponseについてのMozillaの公式ドキュメント

  • 「fetch()」メソッドの処理の中で、リソースへのリクエストに対するレスポンス(Responseオブジェクト)を受け取る。
  • 「fetch()」メソッドはPromiseベースなので、Promiseとしてレスポンスを処理する。
  • レスポンスは、「fetch()」メソッドでリクエストを送信してPromiseがresolve(解決)されたときに返値として渡される。
  • 渡されるレスポンスの本文(body)はHTTPレスポンスの全体なので、「Response」オブジェクトのメソッドを使用して指定の形式に変換して取得する。
  • レスポンスデータは、指定の形式で解釈した結果でresolve(解決)する第2のPromiseとして返される。

 

[ Responseオブジェクトのメソッド ]

受け取ったレスポンスのデータ形式を変換する「Response」オブジェクトのメソッド。

  • arrayBuffer() : ArrayBufferオブジェクト。
  • blob() : Blobオブジェクト。
  • formData() : FormDataオブジェクト。
  • json() : JSON。
  • text() : 文字列。

※ レスポンスの本文を、指定した形式でresolve(解決)するPromiseを返す。

※ 複数のメソッドを指定することはできない。(レスポンスの本文はすぐに解決して処理されるので、最初に指定したメソッドのみが有効。)

 

[ Responseオブジェクトのプロパティー ]

「Response」オブジェクトが持つ主なプロパティー。

  • ok : HTTPステータスコードをチェックし、200番台なら「true」を返し、それ以外なら「false」を返す。
  • status : HTTPステータスコードを整数値で返す。(「200」など。)
  • statusText : HTTPステータスコードに対応したステータスメッセージを返す。 (「200」なら「OK」など。)

 

(6) fetch()メソッド

「fetch()」メソッドを使用して、指定した「取得したいリソースのパス」にリクエストを送信し、レスポンスとしてリソースを取得する。

※ fetch()メソッドについてのMozillaの公式ドキュメント

  • 第1引数に「取得したいリソースのパス」、第2引数に「リクエスト内容を設定するオプション」を指定する。(オプションは省略可。)
  • 「fetch()」メソッドはWindowOrWorkerGlobalScopeミックスインのメソッドで、ネットワークからリソースを取得するプロセスを開始し、レスポンスが利用できるようになったらPromiseを返す。
  • 送信したリクエストに対するレスポンスを表す「Response」オブジェクトでresolve(解決)するPromiseを返す。
  • Promiseなので、「then()」メソッドの処理がresolve(解決)されたときにレスポンス(Responseオブジェクト)を受け取る。
  • 「fetch()」メソッドで返されるPromiseは、レスポンスが「404」や「500」などのHTTPエラーステータスの場合でもreject(拒否)されない。
  • 何かがリクエストの完了を妨げた場合はTypeErrorでPromiseがreject(拒否)される。(リクエスト先が存在しない場合やネットワーク障害があった場合など。)

 

[ 指定できるオプション ]

「fetch()」メソッドの第2引数のオプション指定で、送信するリクエストの内容を設定できる。(省略可。)

  • method : HTTPメソッド。POST、PUT、DELETEなど。(既定値は「GET」。)
  • headers : リクエストに追加するヘッダー。Headersオブジェクトかオブジェクトリテラルで指定する。
    body : リクエストに追加する本文。HTTPメソッドが「GET」や「HEAD」の場合は使用できない。(既定値は「undefined」。)
  • mode : 使用するモード。クロスオリジンリクエストにどう対応するかの設定。(モダンブラウザの既定値は「cors」。Fetchで「cors」を指定しても、送信先サーバー側でクロスオリジンリクエストに対応する設定がされていなければエラーになる。)
  • credentials : 認証情報の送信設定。クロスオリジンでCookieと認証情報を送信する場合は設定が必要。
  • cache : 使用するキャッシュモード。(既定値は「default」。)
  • redirect : リクエスト先でリダイレクトが発生した場合の対応設定。(既定値は「follow」でリダイレクトに従う。)
  • referrer : 送信するリファラーの内容を設定。
  • referrerPolicy : リファラーを送信するかの設定。(既定値は「no-referrer-when-downgrade」で「SSLサイトから非SSLへのリクエスト」以外は常にリファラーを送信。)
  • integrity : 送信するハッシュ値の設定。設定した場合、Fetch自身がリソースのハッシュ値と比較チェックを行い、一致しなければエラーになる。(「sha256-abcdef1234567890」のようなハッシュをintegrityの値に設定する。)
  • keepalive : リクエスト処理中にドキュメントがアンロードされた場合、バックグラウンドでリクエストを継続するかの設定。(既定値は「false」なので継続しない。)
  • signal : リクエスト処理中にキャンセルさせるための設定。キャンセル処理を実装したい場合は、事前に「AbortController」オブジェクトを生成して「signal」に設定しておく必要がある。(後述。)

 

[ fetch()メソッドの記述方法 ]

「fetch()」メソッドでリソースを取得する基本的な記述方法の例。

  1. 第1引数に「取得したいリソースのパス」、第2引数に「リクエストの内容を設定するオプション」を指定。
  2. Promiseの「then()」メソッドの処理でレスポンス(Responseオブジェクト)を受け取る。
  3. レスポンスの「ok」プロパティーをチェックして、「false」(HTTPステータスコードが200番台以外)の場合はエラーを表示。
  4. 受け取ったレスポンスを任意のデータ形式(下記ではテキスト形式)で取得してPromiseを解決。(取得して解決した段階で次のメソッドに移行するので、ここではデータを参照できない。)
  5. 変換したレスポンスのデータを第2のPromiseで受け取って表示。
fetch('取得したいリソースのパス', {
  //オプションを指定
  //POSTでデータを送信したい場合
  method : 'POST',
  body: '送信する本文データ'
}).then((response)=>{
  //ステータスコードがエラーの範囲の場合
  if(!response.ok) throw new Error('Network Response Error.');

  //処理...

  //指定の形式でレスポンスを取得してPromiseを解決して渡す
  return response.text();//テキスト形式でレスポンスを取得
}).then((result)=>{
  console.log(result);//取得したレスポンス
  //処理...
}).catch((error)=>{
  //エラーの場合
  console.error('Fetch Request Error :', error);
});

 

(7) リソースのダウンロード進行状況を取得

「fetch()」メソッドのレスポンスの受信処理の中で、リソースのダウンロードの進行状況を取得する方法。

 

[ Streams APIを利用 ]

ネットワーク経由で受信するリソースをストリーミングとして小さなchunk(断片)に分割し、少しずつ処理することができるStreams APIを利用する。

※ Streams APIのMozillaの公式ドキュメント

  • Streams APIは、JavaScriptがネットワーク経由で受信したデータのストリームにアクセスし、任意の処理をするための機能を持ったインターフェイスを提供する。
  • フェッチ要求によって返された応答のBodyは、デフォルトでStreams APIの「ReadableStream」として公開されるので、リーダーを作成してバイトデータを読み取ることができる。
  • 「ReadableStream」インターフェイスの「getReader()」メソッドで、レスポンスの実体を読み取るためのリーダーを作成できる。(「ReadableStream」はバイトデータの読み取り可能なストリーム。)
  • 「getReader()」メソッドで引数を指定しない場合は、既定値の「ReadableStreamDefaultReader」オブジェクトで返される。(フェッチ要求など、ネットワークから提供されたストリームデータを読み取るために使用できるデフォルトのリーダー。)
  • 「ReadableStreamDefaultReader」インターフェイスの「read()」メソッドで、現在のストリームの内部キューで次に待機しているchunk(断片)を読み込める。(「read()」メソッドはPromiseで処理される。)
  • 「read()」メソッドは、値(「{ done: false, value: 断片のバイト列 }」という形式のオブジェクト)を受け取ったときにresolve(解決)するPromiseを返す。(「done」はBoolean型、「value」はUint8Arrayのバッファーが格納される。)
  • 「read()」メソッドですべての断片が読み込まれてストリームが閉じられるときは、「{ done: true, value: undefined }」という形式のオブジェクトが値として返される。

 

[ ダウンロードの進行状況の表示例 ]

リソースの断片を受信する度に現在のダウンロード済みの合計サイズを算出してコンソールに進行状況を表示する。

  1. レスポンスヘッダーの「’Content-Length’」を参照して、「レスポンス本体の総サイズ」を取得。
  2. 「ReadableStream」インターフェイスの「getReader()」メソッドで、レスポンス本体のリーダーを作成。
  3. 「ReadableStreamDefaultReader」インターフェイスの「read()」メソッドで、ストリームの次の断片を読み込む。
  4. 「read()」メソッドがresolve(解決)したら、値として返されるオブジェクトを取得。
  5. 「受信したデータの断片」のサイズを加算して、ダウンロード済みの合計サイズを取得。
  6. 各断片は配列として保持。
  7. 「レスポンス本体の総サイズ」と「受信済みの断片の総サイズ」から、現在の進行状況を計算してコンソールに出力。
  8. すべての断片の受信が完了したら、配列として保持している各断片を結合してひとつのデータに戻す。
  9. 結合したデータで「fetch」をresolve(解決)する。
fetch('取得したいリソースのパス', {
  method : 'POST',
  body: '送信する本文データ'
}).then(async (response)=>{
  //レスポンスヘッダーからレスポンス本体のサイズを取得
  const totalSize = response.headers.get('Content-Length');
  //レスポンス本体のリーダーを作成
  const ag2reader = response.body.getReader();
  //ダウンロード済みの合計サイズを保持
  let receivedSize = 0;
  //ダウンロード済みの断片を保持する配列
  let chunks = [];

  //最後の断片のダウンロード完了までループ処理
  while(true){
    //読み取りが完了したら値を保持
    const {done, value} = await ag2reader.read();

    //すべての断片をダウンロードしたらループ終了
    if(done) break;

    //ダウンロード済みの断片のバイトサイズの総計
    receivedSize += value.length;
    //ダウンロードした断片を配列で保持
    chunks.push(value);

    //進行状況をコンソールに表示
    let ag2percent = Math.round((receivedSize / totalSize) * 100);
    console.log('Download Progress :');
    console.log(receivedSize + ' / '+totalSize + ' bytes ('+ag2percent+' %)');
  }

  //断片を連結して1つのバッファーに戻す処理
  //ダウンロードした断片の総計と同バイトサイズのArrayBufferを作成
  let ag2data = new Uint8Array(receivedSize);
  //現在の位置
  let arrayPos = 0;
  //断片の数だけ連結を繰り返す
  for(let chunk of chunks){
    ag2data.set(chunk, arrayPos);
    arrayPos += chunk.length;
  }
  //バッファーのバイト列を文字列にデコード
  const ag2response = new TextDecoder().decode(ag2data);

  //レスポンスを返して解決
  return ag2response;
}).then((result)=>{
  console.log(result);
}).catch((error)=>{
  console.error('Fetch Request Error :', error);
});

配列.push(‘値’) : 指定した値を配列の末尾に要素として追加し、追加したあとの配列の要素の数を返す。値は「,」区切りにして複数指定もできる。

Math.round(‘数値’) : 指定した数値をもっとも近似の整数に四捨五入した数値にして返す。

Uint8Array() : 型付き配列のコンストラクター。8ビット符号なし整数値の配列を生成して返す。(引数に長さを指定した場合、中身が「0」で初期化された指定長のバッファーが生成される。)

型付き配列.set(‘配列または型付き配列’, ‘オフセット’) : 指定した配列のコピーを、型付き配列の指定したオフセットのインデックス位置から書き込む。指定の位置に要素が存在する場合は上書きされる。「オフセット」を省略した場合はインデックスの「0」の位置から書き込まれる。

TextDecoder(‘エンコード方式’) : TextDecoder(UTF-8, ISO-8859-2, KOI8-R, GBK等のテキストエンコーディングのデコーダー)のコンストラクター。引数で指定したエンコード方式での新たに生成したテキストデコーダーを返す。引数は省略可能で既定値は「utf-8」。

テキストデコーダー.decode(‘バッファー’) : 指定したバッファーをデコードした文字列を返す。

 

(8) Fetchのキャンセル

「fetch()」メソッドを実行したあと、そのリクエストの処理中にキャンセルさせる方法。

 

[ AbortControllerを利用 ]

リクエストをいつでも中断することを可能にするコントローラーオブジェクトの「AbortController」を利用する。

※ AbortControllerのMozillaの公式ドキュメント

  • 「AbortController()」コンストラクターで「AbortController」オブジェクトのインスタンスが生成できる。
  • 「AbortController」オブジェクトの「signal」プロパティーに「AbortSignal」オブジェクトが設定できる。
  • 「AbortSignal」オブジェクトは、FetchなどのDOM要求と通信し、中断することを可能にするシグナルオブジェクト。

 

[ キャンセル処理の例 ]

「AbortController」オブジェクトを利用してキャンセル処理を実装する。

  1. 「AbortController()」コンストラクターで「AbortController」オブジェクトを生成。
  2. 「fetch()」メソッドのオプションの「signal」に、生成した「AbortController」オブジェクトの「signal」プロパティーを設定しておく。
  3. Fetchを実行。
  4. Fetchの処理中に、キャンセルしたいタイミングで「AbortController」オブジェクトの「abort()」メソッドを実行すると「abort」イベントが発火する。
  5. 登録してある「signal」で「abort」イベントを捕捉するとFetchの処理がキャンセルされる。
  6. 処理がキャンセルされたときのPromiseは「AbortError」というエラーでreject(拒否)される。
//AbortControllerオブジェクトを生成
const ag2controller = new AbortController();

//通常のfetchメソッドの記述
fetch('取得したいリソースのパス', {
  method : 'POST',
  body: '送信する本文データ',
  signal: ag2controller.signal
}).then((response)=>{
  return response.text();
}).then((result)=>{
  console.log(result);
}).catch((error)=>{
  if(error.name === 'AbortError'){
    console.error('Fetch Request Error : 処理はキャンセルされました。');
  }else{
    console.error('Fetch Request Error :', error);
  }
});

//Fetchの中止を実行
ag2controller.abort();

 

 

XMLHttpRequest

(1) XMLHttpRequest

「XMLHttpRequest」は、JavaScriptで非同期(または同期)でHTTPリクエストを行うための組み込みのブラウザオブジェクト。

※ XMLHttpRequestについてのMozillaの公式ドキュメント

 

(2) XMLHttpRequestの要約

  • 「XMLHttpRequest」が「XML」で命名されているのは、実装当時の非同期データ交換の主要な形式が「XML」であったため。(「XML」文書だけでなく、あらゆる種類のデータを扱うことができる。)
  • 「XMLHttpRequest」オブジェクトが持つメソッドを使用して、HTTP通信の「リクエスト内容の設定」、「リクエストの送信」、「レスポンスの取得」などが実行できる。
  • 新しい「XMLHttpRequest」オブジェクトを生成するには「XMLHttpRequest()」コンストラクターを使用する。
  • リクエストヘッダーの内容を指定しなければ、「Content-Type」は自動的に「multipart/form-data」が設定される。
  • 送受信の処理が完了すると、「XMLHttpRequest」オブジェクトに「レスポンスのHTTPステータスコード」や「レスポンス本文」などの情報が格納される。
  • リクエスト送信を実行したあと、通信が成功した場合に発生する「load」イベントを捕捉することでレスポンス内容を取得して処理することができる。
  • レスポンス内容のデータを文字列以外の形式で受け取りたい場合、「responseType」プロパティーで形式を指定できる。(既定値は「’text’」で文字列。後述。)
  • 「progress」イベントを捕捉して処理することで、アップロードとダウンロード、両方の進行状況を確認できる。
  • 「リクエスト先のURL」が「呼び出し元のスクリプト」と同一オリジンでない場合、「XMLHttpRequest」のリクエストは失敗する。
  • クロスドメインリクエストの場合、リクエスト先のサーバーからのHTTPレスポンスヘッダー「Access-Control-Allow-Origin」に自ドメインが設定されていて、呼び出し元として許可されている必要がある。
  • クロスドメインリクエストで認証情報の送受信に対応したい場合は、「withCredentials」プロパティーを「true」に設定してから実行する。(リクエスト先のサーバーからHTTPレスポンスヘッダー「Access-Control-Allow-Credentials : true」が返される必要がある。)
  • リクエスト先でベーシック認証の認証が必要な場合は、「open()」メソッドのオプションの引数で「ユーザーID」と「パスワード」を設定する。
  • 「open()」メソッドの第3引数(「async」の設定)を「false」に指定すると同期で処理される。既定値は「true」で非同期。(同期通信は、JavaScriptの他の処理を一時的に止めてしまうので基本的に使われない。)

 

(3) XMLHttpRequestの処理の流れ

  1. 「XMLHttpRequest」オブジェクトを作成。
  2. 「open()」メソッドで初期化。(「リクエスト先のURL」など、リクエストのメインのパラメータを設定。)
  3. 必要があれば、「タイムアウト」の処理や「リクエストヘッダー」、「レスポンスタイプ」などの設定。
  4. 「send()」メソッドでリクエストを送信。(POST送信の場合、引数に「リクエストボディー」を指定。)
  5. 必要があれば、「progress」イベントを捕捉して、アップロードやダウンロードの進行状況を取得して確認。
  6. 「load」イベントを捕捉して、「response」プロパティーからレスポンスデータ本文を取得。

※ 「open()」メソッドは接続をオープンするわけではなく、リクエストを設定するだけ。ネットワーク処理は「send()」メソッドでのみ始まる。

※ タイムアウトを設定し、リクエストが指定時間内で成功しない場合、「load」イベントと「error」イベントは発生せず「timeout」イベントが発生する。

 

[ XMLHttpRequestの基本的な記述の例 ]

  1. 「XMLHttpRequest()」コンストラクターで「XMLHttpRequest」オブジェクトを生成。
  2. 「open()」メソッドで「通信メソッド」と「リクエスト先のURL」を設定。
  3. 「timeout」プロパティーにリクエストのタイムアウト時間を設定。
  4. 「load」イベントでリスナー登録をして、レスポンスの読み込みが完了した場合の処理を記述。
  5. 「status」プロパティーでレスポンスのHTTPステータスコードを確認。(「200」以外はエラーとして処理。)
  6. 「response」プロパティーでレスポンス本文を取得して表示。
  7. 「error」イベントでリスナー登録をして、通信障害やURLが正しくないなどでリクエストがエラーになった場合の処理を記述。
  8. 「send()」メソッドを実行してリクエスト。

※ 各処理はリクエスト送信をする前に定義されていなければいけないので、「send()」メソッドより前に実行されるように記述する必要がある。(基本的に「send()」メソッドは一番最後に記述するようにする。)

//XMLHttpRequestを作成
const ag2xhr = new XMLHttpRequest();
//open()メソッドで初期化
ag2xhr.open('GET', 'リクエスト先のURL');

//リクエストのタイムアウト(ミリ秒)
ag2xhr.timeout = 30000; //30秒
ag2xhr.addEventListener('timeout', function(){
  console.log('xhr Request timeout.');
});

//受信完了後にレスポンスを取得
ag2xhr.addEventListener('load', function(){
  if(ag2xhr.status !== 200){
    //レスポンスのHTTPステータスコードがエラーの範囲の場合
    console.log('Server Error :');
    console.log(ag2xhr.status);//レスポンスのHTTPステータスコード
    console.log(ag2xhr.statusText);////HTTPステータスメッセージ(文字列)
  }else{
    console.log(ag2xhr.response);//受け取ったレスポンス本体
  }
});
//エラーが発生した場合
ag2xhr.addEventListener('error', function(){
  console.log('xhr Request error.');
});

//send()メソッドでリクエスト送信
ag2xhr.send();

 

(4) HTTPヘッダーの設定と確認

送信時のリクエストヘッダーの設定と、受信時のレスポンスヘッダーの内容の確認方法。

 

[ 送信時 ]

送信時は、「setRequestHeader()」メソッドを使用してヘッダーを設定し、カスタムヘッダーを送信できる。

ag2xhr.setRequestHeader('Content-Type', 'application/json');

setRequestHeader(‘ヘッダー名’, ‘値’) : 指定したヘッダー名と値のリクエストヘッダーを設定する。複数記述した場合は、上書きではなく追加で設定される。一度設定した場合、削除はできない。

 

[ 受信時 ]

受信時は、「getResponseHeader()」メソッドまたは「getAllResponseHeaders()」メソッドを使用して、レスポンスからヘッダーを読み取ることができる。

ag2xhr.getResponseHeader('Content-Type');

getResponseHeader(‘ヘッダー名’) : 指定したヘッダー名の値を返す。(「Set-Cookie」と「Set-Cookie2」は除く。)

getAllResponseHeaders() : 「Set-Cookie」と「Set-Cookie2」を除く、すべてのレスポンスヘッダーを返す。ヘッダー間の改行は、OSに依存せず常に「”\r\n”」。名前と値のセパレータは常にコロンとスペース「: 」。

 

(5) レスポンスタイプの設定

「responseType」プロパティーにレスポンスタイプを設定しておくことで、レスポンス本文を任意の形式で受け取ることができる。

※ responseTypeについてのMozillaの公式ドキュメント

  • : 空文字を指定した場合は「’text’」と同じ扱い。既定値である「文字列」で取得。
  • ‘text’ : 「文字列」(「DOMString」オブジェクトに入ったテキスト)で取得。(既定値の設定。)
  • ‘arraybuffer’ : 「ArrayBuffer」オブジェクトで取得。
  • ‘blob’ : 「Blob」オブジェクトで取得。
  • ‘document’ : 受信したデータのMIME タイプに基づいて、「HTMLのDocument」か「XMLのXMLDocument」のどちらかで取得。
  • ‘json’ : 「JSON」で取得。 (自動的にパースされる。)

 

(6) アップロードとダウンロードの進行状況を取得

アップロードは「XMLHttpRequestUpload」オブジェクト(「XMLHttpRequest」オブジェクトの「upload」プロパティー)、ダウンロードは「XMLHttpRequest」オブジェクトで「progress」イベントを捕捉し、その時点での転送量を取得して進行状況を算出する。

 

[ progressイベント ]

「progress」イベントは通信時に定期的に発火し、「progress」イベントが持つ下記のプロパティーを利用して進行状況を算出することができる。

  • event.lengthComputable : ヘッダーに「Content-Length」が設定されている場合に「true」を返す。「Content-Length」が総サイズを示すので、ヘッダーに含まれていなければ進行状況を測定できない。
  • event.loaded : 転送済みの断片データのバイトサイズを返す。
  • event.total : 転送されるデータの全体のバイトサイズを返す。(「Content-Length」の値。)

※ ヘッダーやその他のオーバーヘッドを除いたHTTPメッセージの本文のみをカウントしたサイズが返される。

 

[ アップロードの進行状況の取得 ]

ag2xhr.upload.addEventListener('progress', function(event){
  console.log('Upload Progress :');
  if(event.lengthComputable){
    let ag2percent = Math.round((event.loaded / event.total) * 100);
    console.log(event.loaded + ' / '+event.total + ' bytes ('+ag2percent+' %)');
  }else{
    console.log(event.loaded + ' bytes');
  }
});

//アップロードが完了したとき
ag2xhr.upload.addEventListener('load', function(){
  console.log('Upload is done successfully.');
});

Math.round(‘数値’) : 指定した数値をもっとも近似の整数に四捨五入した数値にして返す。

 

[ ダウンロードの進行状況の取得 ]

ag2xhr.addEventListener('progress', function(event){
  console.log('Download Progress :');
  if(event.lengthComputable){
    let ag2percent = Math.round((event.loaded / event.total) * 100);
    console.log(event.loaded + ' / '+event.total + ' bytes ('+ag2percent+' %)');
  }else{
    console.log(event.loaded + ' bytes');
  }
});

 

(8) XMLHttpRequestのキャンセル

「XMLHttpRequest」のリクエストは「abort()」メソッドを実行すればいつでもキャンセルできる。

ag2xhr.abort();

 

 

この記事のURL

https://memo.ag2works.tokyo/post-4424/

Copyコピー
この記事のタイトル

FetchとXMLHttpRequestの基礎知識。 | memo メモ [AG2WORKS]

Copyコピー
この記事のリンクタグ

<a href="https://memo.ag2works.tokyo/post-4424/" target="_blank" rel="noopener">FetchとXMLHttpRequestの基礎知識。 | memo メモ [AG2WORKS]</a>

Copyコピー
※ フィールドをクリックでコピーするテキストの編集ができます。

この記事へのコメント

コメントの書き込みはまだありません。

  • コメント内のタグはエスケープ処理され、文字列として出力されます。
  • セキュリティーのため、投稿者のIPアドレスは取得されます。
  • 管理者が内容を不適切と判断したコメントは削除されます。
  • このフォームにはスパム対策として、Googleの提供するreCAPTCHAシステムが導入されています。
    (Google Privacy Policy and Terms of Service.)