1. Qiita
  2. 投稿
  3. HTML5

お疲れさまXMLHttpRequest、こんにちはfetch

  • 579
    いいね
  • 0
    コメント

Service Workersでプッシュ通知を受信できるようになったわけですが([1]: GCM)([2]: Web Push)、Chromeではバージョン48まではGCMで通知だけができるようになっただけで、メッセージ本体は通知を受けてからService Workerでサーバから改めて受け取るような実装をする必要があったりします。

ここで、Service Workersでは、XMLHttpRequestが使えません。その代わり、XMLHttpRequest (以下、XHR)に代わるWHATWGの仕様としてFetch APIがあり、Service WorkersではこのFetch APIを使うことになっていますので、その使い方を簡単に紹介します。

Fetch API自体は、Service Workers専用のものではなく、メインスレッドでもXHRの代わりに使うことが可能です。現時点で実装しているブラウザは、Service Workersが使えるChrome, Opera, Firefox等(Chrome, Opera等はごく一部の古いバージョンでは非対応)と、バージョン14以降のMicrosoft Edgeとなります。一方、Safariでも実装が進んでおり、Technology Preview 12以降で有効化されています(正式リリース版では未対応です)。

対応・非対応ブラウザが混在する状況がまだしばらく続きますので、Polyfillを使うと良いかもしれません。

基本的な使い方

まず、同じオリジン内のリソースをHTTP(S)のGETで単純に取得する場合は、次のようにします。

fetch('URLあるいは相対パスなど').then(...);

fetch()の結果はPromiseで返され、resolve関数には引数としてResponseオブジェクトが渡されます。ここで、取得した内容はBlob, ArrayBuffer, JSON, プレーンテキストのいずれかの形で受け取ることが出来ます。

Blobで受け取る場合
fetch(url).then(function(response) {
  return response.blob();
}).then(function(blob) {
  // blobにBlob型で結果が渡される
  ...
});
ArrayBufferで受け取る場合
fetch(url).then(function(response) {
  return response.arrayBuffer();
}).then(function(arrayBuffer) {
  // arrayBufferにArrayBuffer型で結果が渡される
  ...
});
JSONで受け取る場合
fetch(url).then(function(response) {
  return response.json();
}).then(function(json) {
  // jsonにJSONオブジェクトで結果が渡される
  ...
});
プレーンテキストで受け取る場合
fetch(url).then(function(response) {
  return response.text();
}).then(function(text) {
  // textに文字列で結果が渡される
  ...
});

様々なオプションを使う

fetch()メソッドでは、JSONで様々なオプションを渡すことで、HTTPリクエストを柔軟にカスタマイズできるのが大きな特徴です。JSONで一度に指定できるのでXHRよりも扱い方が楽になっています。

fetch('URLあるいは相対パスなど', {/* JSONでオプションを指定 */}).then(...);

POSTメソッド

オプションのmethodメンバにHTTPメソッド名を指定することが出来ます。また、メソッドが'POST'の場合は、bodyメンバにアップロードしたいデータを指定することができます。

POSTメソッドの例(フォームデータのアップロード)
fetch(url, {
  method: 'POST',
  body: new FormData(document.getElementById('<form>タグのid'))
}).then(function(response) {
  return response.json();
}).then(function(json) {
  ...
});
POSTメソッドの例(ファイル(Blob)のアップロード)
fetch(url, {
  method: 'POST',
  body: document.getElementById('<input type="file">タグのid')).files[0]
}).then(function(response) {
  return response.json();
}).then(function(json) {
  ...
});

クロスオリジンとクレデンシャル

CORSによるクロスオリジン接続を行いたい時は、オプションのmodeメンバに'cors'を指定します。なお、'same-origin'を指定すると同一オリジン以外の接続がエラーとなり、'no-cors'を指定するとクロスオリジン接続ができない場合にエラーとならずに空のレスポンスが返されます。

クロスオリジン接続の例
fetch(url, {
  mode: 'cors'
}).then(function(response) {
  return response.json();
}).then(function(json) {
  ...
});

また、デフォルトではクッキー等のクレデンシャルをHTTPリクエストに含めないようになっています。クッキー等を付けたい場合は、次のようにします。

クロスオリジン接続にクッキーを付けたい場合の例
fetch(url, {
  mode: 'cors',
  credentials: 'include'
}).then(function(response) {
  return response.json();
}).then(function(json) {
  ...
});

任意のHTTPリクエストヘッダを追加

オプションのheadersフィールドにHeadersオブジェクトもしくはJSONでHTTPリクエストヘッダの内容を指定することができます。

HTTPリクエストヘッダに任意のヘッダを追加する例(1)
fetch('https://www.googleapis.com/userinfo/v2/me', {
  headers: { 'Authorization': 'Bearer: (アクセストークン)' }
}).then(function(response) {
  return response.json();
}).then(function(json) {
  ...
});
HTTPリクエストヘッダに任意のヘッダを追加する例(2)
fetch('https://www.googleapis.com/userinfo/v2/me', {
  headers: new Headers({ 'Authorization': 'Bearer: (アクセストークン)' })
}).then(function(response) {
  return response.json();
}).then(function(json) {
  ...
});

Responseオブジェクトのその他の機能

ResponseオブジェクトのheadersプロパティにはHeadersオブジェクトとしてHTTPレスポンスヘッダの内容が格納されます。また、statusプロパティにはHTTPレスポンスのステータスコード(200など)が格納されます。

HTTPレスポンスヘッダを取得する例
fetch(url).then(function(response) {
  console.log(response.headers.get('content-type')); // 例えば、"text/html; charset=utf-8"
  console.log(response.status); // 例えば、200
});

Service Workersでfetch()を使う場合の注意事項

プッシュ通知の受信時の処理などでfetch()を使う場合、結果を受け取る処理のPromiseをevent.waitUntil()メソッドに渡す必要があります。そうしなければ、fetch()の受信処理が完了しないうちにService Workerが停止してしまいますので、ご注意下さい。

ServiceWorkersでfetch()を利用する例
self.addEventListener('push', function(evt) {
  var getEndpoint = self.registration.pushManager.getSubscription();
  var confirmPush = getEndpoint.then(function(subscription) {
    var form = new FormData();
    form.put('endpoint', subscription.endpoint);
    return fetch(pushServerUrl, {
      method: 'POST',
      body: form
    });
  });
  var getPushData = confirmPush.then(function(response) {
    return response.json();
  });
  var parseJSON = getPushData.then(function(json) {
    var data = json.data;
    ...
  });
  evt.waitUntil(parseJSON);
});