JavaScript
HTML5
Chrome
firefox
ServiceWorker

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

More than 1 year has passed since last update.

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 10.1以降となります。

なお、過去のバージョンのブラウザへの対応が必要となる場合は、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.append('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);
});