2018/09/07更新

[フロントエンド] fetchを用いたAjax通信を行う

このエントリーをはてなブックマークに追加      

こんにちは、@yoheiMuneです。
XMLHttpRequestに変わる次のAjax仕様としてfetchが策定されていますが、今日はそれを扱ったAjax通信をブログに書きたいと思います。

画像

目次




fetchとは

fetchとはwhatwgで策定されている、フロントエンドJavaScriptの次世代の非同期通信の規格で、XMLHttpRequestに取って代わるものです。例えば以下のような使い方ができます。
fetch('/user/1').then(response => {
    console.log(response.status); // 200
    return response.json();
}).then(json => {
    console.log('json:', json); // json : { name : 'Yohei' }
});
XMLHttpRequestに比べてシンプルです。現時点でもfetch | Githubで提供されているポリフィルを用いて利用可能ですので、今日はそれを使ってみたいと思います。

また、fetchはPromiseを使った実装となります。Promiseについては「[Javascript] Promise/A+仕様を、チュートリアル形式で詳しく解説します」をご参照ください。



fetchのポリフィルの導入

fetch | Githubで公開されているfetchのポリフィルは、bowserまたはnpmでインストール可能です。例えばBowerを使ってインストールするには以下のように行います。
$ bower init
$ bower install --save fetch
これでインストールしたものをHTMLから読み込めばOKです。今回は以下のようなHTMLを使います。
<html>
    <body>
        <script type="text/javascript" src="./bower_components/fetch/fetch.js"></script>
        <script type="text/javascript" src="./fetch-sample.js"></script>
    </body>
</html>



サンプルコード

今回のサンプルコードはこちらおきました。サーバー側はNodeのExpressを利用していて、起動は以下のように行います。
$ bower install
$ npm install
$ node server.js # => http://localhost:3000



fetchのいろいろな使い方

さてここから本題ですが、fetchについて主だった使い方を書きたいと思います。


基本的な使い方

以下のような形式で利用することができます。
fetch(url, options).then(function(response) {
  // handle HTTP response
}, function(error) {
  // handle network error
});
urloptionsを指定してfetchを呼び出し、結果はPromiseベースで受け取ります。レスポンスには例えば以下のようなものが含まれます。
fetch(url, options).then(function(response) {
    // ステータスコード
    console.log(response.status); // 200
    // ステータス(文字列)
    console.log(response.statusText); // 'OK'
    // レスポンスヘッダー
    console.log(response.headers); //  {} : オブジェクト形式で取得可能
    // URL
    console.log(response.url); // http://localhost:3000/sample
});
具体的な使い方は以降で見ていきたいと思います。


結果をテキストファイルやHTMLで受け取る

レスポンス結果をテキストベースで受け取る場合には、response.text()で取り出します。
fetch('/text').then(response => {

    // textやhtmlなどの文字列で値を受け取る場合は、textメソッドを呼びます.
    // このメソッドはPromiseを返却します。
    return response.text();

}).then(text => {

    // Promiseが解決されたら値が取得できます.
    console.log('text:', text); // text: 鈴木さん
});


結果をJSONで受け取る

レスポンスデータをJSONで受け取る場合には、response.json()を使います。
fetch('/json').then(response => {

    // JSONで値を受け取る場合は、jsonメソッドを使います。
    // このメソッドはPromiseを返却します。
    return response.json();
}).then(jsonData => {
    console.log('jsonData:', jsonData); // jsonData: Object {name: "鈴木さん"}
});
ここでは内部的にJSON.parseが実行されます。例えばレスポンスボディが空の場合にはエラーになるので、少しだけ注意が必要です。


400や500エラーもresolvedされる

fetchの特徴として、HTTPステータスが400系や500系エラーでも、処理はエラーにならず、Promiseはresolvedになります。
// 400や500系のステータスでも、resolvedで処理される.
fetch('/400').then(response => {
    console.log('status: ', response.status);
});
fetch('/500').then(response => {
    console.log('status: ', response.status);
});
ステータスコードでの処理振り分けが必要な場合には、それぞれ個別に実装します。


ネットワークエラーなどでは失敗する

ネットワークエラーなどの場合にだけ、fetchのPromiseはrejectedになります。サンプルコードの場合には、画面を表示した後にNodeサーバーを落とした状態でfetchすると再現できます。
// ネットワークエラーなどの時だけ失敗する。(ローカルサーバー落とすとかしてテスト)
fetch('/NETWORKERROR').then(res => {
    console.log('resolved'); // resolvedにはならない.
}).catch(err => {
    console.log('rejected:', err); // rejected: TypeError: Failed to fetch
});
なお、パス間違い404になった場合はこのエラーにはならずresolvedされますので、注意です。fetchがrejectedになるのは、フロント的にはもうどうすることもできないfatal状態といった感じでしょうか。


GETパラメータを付与する

GETパラメータはURLに付与して送ります。
// URLにパラメータをつけて送ります.
fetch('/get_with_params?id=1&name=yohei')
    .then(response => response.text())
    .then(text => console.log(text)); // params.id=1, params.name=yohei
また、URLSearchParamsを使ってURLクエリを作成することもできます。
// GETの場合のパラメータの送り方①:URLSearchParamsを利用
// https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams
const params = new URLSearchParams();
params.set('id', 2);
params.set('name', 'munerin');
fetch('/get_with_params?' + params.toString())
    .then(response => response.text())
    .then(text => console.log(text));


POSTデータを送る(JSON形式)

POSTでデータを送る場合には、options.bodyに値を設定します。またContent-Typeヘッダーを指定します。
// POSTで送る場合(JSON形式)
fetch('post_with_params', {
    method : 'POST',
    body : JSON.stringify({ id : 3, name : 'matsu' }), // 文字列で指定する
    headers : new Headers({ "Content-type" : "application/json" })
}).then(response => response.text())
    .then(text => console.log(text)); // params.id=3, params.name=matsu
なお、Bodyに指定する値によって、以下の値がデフォルト値としてContent-Typeに指定されます。
クラスContent-Typeのデフォルト値
Stringtext/plain;charset=UTF-8
URLSearchParamsapplication/x-www-form-urlencoded;charset=UTF-8
FormDatamultipart/form-data
Blobinherited from the blob.type property
https://github.github.io/fetch/#request-body


POSTデータを送る(Form形式)

Form形式でも送ることができます。
// POSTで送る場合(FormData形式)
fetch('post_with_params', {
    method : 'POST',
    body : 'id=4&name=shinji',
    headers : new Headers({'Content-type' : 'application/x-www-form-urlencoded' })
}).then(response => response.text())
    .then(text => console.log(text)); // params.id=4, params.name=shinji

optionsで指定できる値

fetchの第2引数に指定するoptionsには以下の項目が指定できます。
  • method (String) - HTTPメソッド。初期値はGET
  • body (String, body types) - レスポンスボディの種類
  • headers (Object, Headers) - HTTPヘッダー。初期値は{}
  • credentials (String) - 認証モードの指定。初期値はomit
    • "omit" - Cookieなど認証情報を付与しない。
    • "same-origin" - 同一ドメインの場合に認証情報を付与する。
    • "include" - 全てのドメインで認証情報を付与する。
https://github.github.io/fetch/#options


Cookieを付与してリクエストを送る

デフォルトではCookieは付与されないので、options.credentialsを設定して送ります。
// Cookieを付与した送信
fetch('/text', { credetials : 'include' })
    .then(response => response.text())
    .then(text => console.log('text:', text));


リクエストヘッダーを付与する

リクエストヘッダーは、options.headersHeadersを付与します。
// ヘッダーの付与
fetch('/text', {
    headers : new Headers({ 'X-MY-APP' : '12345' })
}).then(response => response.text())
    .then(text => console.log('text:', text));



参考資料

さらなる詳しい情報は、以下のドキュメントをご確認ください。

https://github.com/github/fetch

https://github.github.io/fetch/

Fetch 概説 - Web API インターフェイス | MDN



最後に

先月から入ったプロジェクトでfetchが使われていたので腰を据えて学んでみました。低レベルなAPIで色々とできていいですね。今後にちょっとずつ使えたらと思います。

最後になりますが本ブログでは、フロントエンド・Go言語・Node.js・Python・Linux・開発関連・Swift・Java・機械学習など雑多に情報発信をしていきます。自分の第2の脳にすべく、情報をブログに貯めています。気になった方は、本ブログのRSSTwitterをフォローして頂けると幸いです ^ ^。

最後までご覧頂きましてありがとうございました!





こんな記事もいかがですか?

[取り組み] フロントエンドでコーディングスピードをアップさせる6つの方法!と思って書いてたら30個も書いちゃった。
[フロントエンド] フロントエンドの入社試験99問!難しいですよ〜w。
[フロントエンド] Webページを表示するテストの際に、通信速度を3Gに制限して表示してみよう
[フロントエンド] スマホ実機でのデバッグ手段を増やす!Macのプロキシを利用して、通信内容を確認する。
[フロントエンド] Chrome 35 Beta の変更点。Touch制御、新しいJavaScript機能、プレフィックスなしのShadowDOM
[フロントエンド]複数アカウントでのテストには、Chromeのユーザー管理を使って、Cookieを切り替えると便利
[フロントエンド] Chrome36βが出た。変更点など。element.animate、HTML Imports、Object.observe、他。
RSS画像

もしご興味をお持ち頂けましたら、ぜひRSSへの登録をお願い致します。