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

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

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

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

なお、過去のバージョンのブラウザへの対応が必要となる場合は、[Polyfill](https://github.com/github/fetch)を使うと良いかもしれません。

# 基本的な使い方

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

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

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

```js:Blobで受け取る場合
fetch(url).then(function(response) {
  return response.blob();
}).then(function(blob) {
  // blobにBlob型で結果が渡される
  ...
});
```

```js:ArrayBufferで受け取る場合
fetch(url).then(function(response) {
  return response.arrayBuffer();
}).then(function(arrayBuffer) {
  // arrayBufferにArrayBuffer型で結果が渡される
  ...
});
```

```js:JSONで受け取る場合
fetch(url).then(function(response) {
  return response.json();
}).then(function(json) {
  // jsonにJSONオブジェクトで結果が渡される
  ...
});
```

```js:プレーンテキストで受け取る場合
fetch(url).then(function(response) {
  return response.text();
}).then(function(text) {
  // textに文字列で結果が渡される
  ...
});
```

# 様々なオプションを使う

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

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

## POSTメソッド

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

```js:POSTメソッドの例(フォームデータのアップロード)
fetch(url, {
  method: 'POST',
  body: new FormData(document.getElementById('<form>タグのid'))
}).then(function(response) {
  return response.json();
}).then(function(json) {
  ...
});
```

```js: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'`を指定するとクロスオリジン接続ができない場合にエラーとならずに空のレスポンスが返されます。

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

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

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

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

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

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

```js: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など)が格納されます。

```js: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が停止してしまいますので、ご注意下さい。

```js: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);
});
```