これまでFetch APIをなんとなくで使っていてちゃんと理解できていなかったので、改めて調べ直して使い方を理解していこうと思います。
Fetch API概要
そもそもFetch(フェッチ)とはなんなんでしょうか?
フェッチとは、取りに行く、取ってくる、持ってくる、連れてくる、来させる、呼び出す、引き出す、惹きつける、などの意味を持つ英単語。ITの分野では機器やプログラムなどが特定の場所からデータなどを読み出す動作のことを指すことが多い。
(フェッチとは - IT用語辞典 より)
ということは、AjaxなどでAPIを通じてデータを取得する動作もfetchといえそうです。
Fetch APIとは、ページの外部からリソースを取得するためのインターフェースを定義した、Webブラウザの標準APIです。Fetch APIは
fetch
関数など、リソースを取得するためのAPIを定義しています。
([コラム] Fetch API - Promiseを活用する · JavaScriptの入門書 #jsprimer より)
Webブラウザの標準API
というのがネックかと思います。MDNでは下記のように記述されています。
Fetch API を利用すると、リクエストやレスポンスといった HTTP のパイプラインを構成する要素を操作できるようになります。また fetch() メソッドを利用することで、非同期のネットワーク通信を簡単にわかりやすく記述できるようになります。
従来、このような機能はXMLHttpRequest
を使用して実現されてきました。 Fetch はそれのより良い代替となるもので、サービスワーカーのような他の技術から簡単に利用することができます。 Fetch は CORS や HTTP 拡張のような HTTP に関連する概念をまとめて定義する場所でもあります。
(Fetch 概説 - Web API インターフェイス | MDN より)
つまり、XMLHttpRequest
に変わる、モダンで他の技術と連携しやすい(ローレベルな)非同期通信の手段といえます。
対応ブラウザ
IEでは使えません。また Android 4.4.4
、iOS Safari 10.2
以下 も非対応です。
Can I use... Support tables for HTML5, CSS3, etc
そのため上記に対応させるには Pollyfill が必要です。
構文
最も基本的な構文は下記になります。
fetch(input[, init])
第一引数(input
)
fetchしたいリソース(下記の2つが使用可)
- URLを含む
USVString
(基本的には文字列と考えて大丈夫かも。詳細は割愛) Request
オブジェクト
第二引数(init
)
リクエストに適用したい設定を含むオプションの(必須ではない)オブジェクト
オプション | 説明 |
---|---|
method | リクエストするメソッド。 デフォルトは GET |
headers | リクエストに追加したいヘッダー。Headers オブジェクトか ByteString 値を持つオブジェクトリテラルで指定 |
body | リクエストに追加したいボディ。method が GET や HEAD の場合使用できない。Blob 、FormData 、URLSearchParams などが使用可※本文のデータ型は "Content-Type" ヘッダーと一致する必要がある |
mode | リクエストで使用したいモード。クロスオリジンリクエストに対して有効なレスポンスができるか、またレスポンスのプロパティが読み取り可能かどうかを判定するために使用される。cors 、no-cors 、cors-with-forced-preflight 、same-origin が使用可。※クロスオリジンリクエストを許可するには cors (デフォルト)を設定。詳細:Request.mode - Web API インターフェイス | MDN |
credentials | リクエストに使用したい秘密情報。クロスオリジンリクエストの場合、ユーザーエージェントが ほかのドメインから cookie を送信すべきかどうかを示す。omit : 決してクッキーを送信しない。※デフォルトsame-origin : リクエストURLが呼び出し元のスクリプトと同一オリジンだった場合のみ cookie を送信するinclude クロスオリジンの呼び出しであっても常にクッキーを送信する。詳細:Request.credentials - Web API インターフェイス | MDN |
cache | リクエストのキャッシュのモードを指定する。default (デフォルト), no-store , reload , no-cache , force-cache , only-if-cached が使用可 |
referrer | リクエストのリファラを文字列(USVString)で設定する。client (デフォルト)、no-referrer 、任意のurl が指定可。 |
redirect | リダイレクトが発生した場合どう処理するかについて指定する。follow : リダイレクト先まで追従してリソースの取得を行う ※デフォルトerror : リダイレクトが発生した場合ネットワークエラーを投げるmanual : 手動でリダイレクトを制御する |
以下略 | 詳細: GlobalFetch.fetch() - Web API インターフェイス | MDN |
戻り値
解決時にリクエストのレスポンスを表す Response
オブジェクトを取得できる Promise
オブジェクト。
基本的なリクエスト&レスポンスの取得
それでは実際に、一番基本的な GETリクエスト
を行ってみます。
fetch('https://holidays-jp.github.io/api/v1/date.json') .then(response => response.text()) .then(text => { console.log(text); });
祝日一覧APIをコールして、その結果を文字列として出力しています。
処理の流れは下記になります。
fetch(...)
の結果が Promiseオブジェクトして返ってくる- このPromiseの結果は
Response
オブジェクトで、Response.text()
でリクエストの結果を文字列で解決されたPromiseを得ることができる - そして再び
then()
を使って、文字列にアクセスできる
fetch() から返されるPromiseは404でもrejectされない
このPromiseオブジェクトはレスポンスが HTTP 404
や 500
を返してHTTPエラーステータスの場合でも reject
されません。
fetch()
の役割はリクエストを投げてレスポンスを受け取って返すことで「エラーを示すレスポンスを返す」という役割は成功しているからです。
reject
されるのはネットワークのエラーや、何かがリクエストの完了を妨げた場合のみとなっています。
ではどうすれば良いのでしょうか。
Response
オブジェクトには Response.ok
という「レスポンスが成功(200-299 の範囲のステータス)したか否かの真偽値が入っているプロパティがあり、これを参照することで解決できます。
下記がそのサンプルコードです。
fetch('https://holidays-jp.github.io/api/v1/date.jsonnnnnnnnnnnn') // 404 (Not Found) .then(response => { if (response.ok) { return response.text(); } else { return Promise.reject(new Error('エラーです!')); } }) .then(text => { console.log(text); }) .catch(e => { console.log(e.message); // エラーです! });
Response オブジェクト
fetchを使いこなす鍵は Response
オブジェクトにありますので、詳しく見ていきたいと思います。
プロパティ
プロパティ名 | 説明 | 備考 |
---|---|---|
headers | レスポンスに関連した Headers オブジェクト | 読取専用 |
ok | レスポンスが成功(200-299 の範囲のステータス)したか否かの真偽値 | 読取専用 |
redirected | レスポンスがリダイレクトの結果であるかどうかの真偽値 | 読取専用 |
status | HTTPステータスコード | 読取専用 |
statusText | ステータスコードに対応したステータスメッセージ 例)200の場合: OK 404の場合:Not Found etc |
読取専用 |
type | レスポンスのタイプ 例)basic , cors etc) |
読取専用 |
url | レスポンスのURL | 読取専用 |
useFinalURL | レスポンスの最後の URL かどうかの真偽値 | ServiceWorkersのみ適用される。 他では undefined になる |
body | コンテンツのボディを示す ReadableStream の単純なゲッター |
読取専用 |
bodyUsed | レスポンスで body が既に使用されているかどうかの真偽値 | 読取専用 |
メソッド
メソッド名 | 説明 | 備考 |
---|---|---|
arrayBuffer() | ボディテキストを ArrayBuffer として解析した結果で解決されるPromise |
Body のミックスインとして定義されている |
blob() | ボディテキストを Blob として解析した結果で解決されるPromise |
同上 |
formData() | ボディテキストを FormData として解析した結果で解決されるPromise |
同上 |
json() | ボディテキストを Json として解析した結果で解決されるPromise |
同上 |
text() | ボディテキストを 文字列(厳密にはUSVString) として解析した結果で解決されるPromise | 同上 |
以下略 | 詳細:Response - Web API インターフェイス | MDN |
...(略) これはもちろんただの HTTP レスポンスであり、実際の JSON ではありません。 response オブジェクトから JSON を抽出するには、
json()
メソッドを使用する必要があります。(Body
のミックスインとして定義されていて、これはRequest
とResponse
の両オブジェクトに実装されています。)
(Fetch 概説 - Web API インターフェイス | MDN) より
このようにResponse オブジェクトはHTTP レスポンスに関するステータスコード、ヘッダ、本文(ボディ)に関するプロパティやメソッドを持っています。
Response オブジェクトが得られるタイミング
Responseオブジェクトが得られる(= fetch()
の返すPromiseが解決される)のは、レスポンスヘッダが全部返ってきたとき のようです。
HTTPレスポンスは、ステータスコード、次にヘッダ、最後に本文(レスポンスボディ)という順番のため、Responseオブジェクトが得られた瞬間は、まだ本文を取得していません。
そのため、Response.text()
がPromiseを返す仕様となっています。
そしてこのPromiseはレスポンスの本文が全部到着するのを待ってから解決されます。
↑の方のサンプルで利用したResponse.ok
は、ステータスコードに関する情報のため本文関係なくReponseオブジェクトから直に参照できるため、本文の取得前に判定が可能だったというわけですね。
下記がResponseオブジェクトに関してのまとめとなります。
Responseオブジェクトというのは、ヘッダーまでのレスポンスで得られた情報を表すオブジェクトであるというのが1つの側面です。
status
やheaders
などを通してこれらの情報を得ることができます。そして、もうひとつの側面が、これから取得されるであろうレスポンス本文を表すオブジェクトという側面です(仕様書的にはこれを
Body mixin
と読んでいます)。
最初の例で使用したresponse.text()
メソッドはこれに属するメソッドです。
(まだXMLHttpRequestを使ってるの? fetchのすすめ) より
まとめ
調べてみるとfetchに関する情報は多いため今回はごく基本的なところだけになりますが、Response
オブジェクトを中心にfetchでできることを見てみると理解がしやすいように思えました。
レスポンスボディは ストリーム
で扱える点など、まだ掴めきれていないところが多々ありますそれはまた別途学習して掴んでいこうと思います。
Fetch API
には標準でタイムアウト処理やリトライ処理がないので、実際のアプリケーション開発には、axios や superAgent といったライブラリを利用するのが良いかもしれません。
参考
- Fetch 概説 - Web API インターフェイス | MDN
- Fetch の基本コンセプト - Web API インターフェイス | MDN
- GlobalFetch.fetch() - Web API インターフェイス | MDN
- Request - Web API インターフェイス | MDN
- Response - Web API インターフェイス | MDN
- Body - Web API インターフェイス | MDN
- まだXMLHttpRequestを使ってるの? fetchのすすめ - Qiita
- Fetch API 解説、または Web において "Fetch する" とは何か? - Block Rockin’ Codes
- JavaScriptのFetch APIを利用してリクエストを送信する
- [コラム] Fetch API - Promiseを活用する · JavaScriptの入門書 #jsprimer