Sibainu Relax Room

柴犬と過ごす

JavaScript フェッチ API について

フェッチ?? API?? なんかわからない言葉がでてきたぞ真剣に聞くぞという顔をしている柴犬です。

概要

AJAXというものを最近始めてみました。どういうものか調べてみました。

まず、次の公式HPで「AJAX」を見てみました。

https://developer.mozilla.org/ja/docs/Glossary/AJAX

それによると、AJAXは XMLHttpRequest という技術を用いて、ウェブページを構築するプログラミング手法のことのようです。

そして、対話型のウェブサイトや最新のウェブ標準では、AJAX は徐々に Fetch API 標準に置き換えられていると記述されています。

つまり、Fetch API は XMLHttpRequest API をより柔軟で強力な機能を備えたものに置き換えたものとありますので、今日 AJAX は敢えて使われていないようです。

XMLHttpRequest API

XMLHttpRequest API を次の公式HPで調べてみます。

https://developer.mozilla.org/ja/docs/Web/API/XMLHttpRequest_API

WEBアプリケーションがWEBサーバーにJavaScript プログラムで HTTP リクエストを行い、JavaScript プログラムでレスポンスを受け取るために使われるAPIのようです。

使用方法

  • XMLHttpRequest のインスタンスを、コンストラクターを呼び出して作成します。
  • XMLHttpRequest.open(リクエストの URL, HTTP メソッド, オプションでユーザ名ー, パスワード ) を呼び出して初期化します。
  • リクエストの結果を取得するイベントハンドラーを取り付けます。
  • XMLHttpRequest.send() を呼び出してリクエストを送信します。

Fetch API

フェッチ API は(ネットワーク越しの通信を含む)リソース取得のためのインターフェイスを提供しています。

fetch() メソッド init オブジェクト

fetch() はグローバルメソッドで、ネットワークからリソースを取得するプロセスを開始し、レスポンスが利用できるようになったら履行されるプロミス(Promise オブジェクト)を返します。

必須の引数は1つで取得したいリソースのパスを指定します。

2 つ目の引数を適用することができ、init オブジェクトで様々な種類の設定を制御することができます。

init オブジェクトの中身を見てみると

fetch(url, {
    method: "POST", // *GET, POST, PUT, DELETE, etc.
    mode: "cors", // no-cors, *cors, same-origin
    cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
    credentials: "same-origin", // include, *same-origin, omit
    headers: {
      "Content-Type": "application/json",
      // 'Content-Type': 'application/x-www-form-urlencoded',
    },
    redirect: "follow", // manual, *follow, error
    referrerPolicy: "no-referrer", // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
    body: JSON.stringify(data), // 本体のデータ型は "Content-Type" ヘッダーと一致させる必要があります
  })

からコメントアウトを削除すると

fetch(url, {
    method: "POST",
    mode: "cors",
    cache: "no-cache",
    credentials: "same-origin",
    headers: {
      "Content-Type": "application/json",
    },  
    redirect: "follow",
    referrerPolicy: "no-referrer",
    body: JSON.stringify(data),
  })

更に絞り込むと次のようになります。

init オブジェクト

{
    method: "POST",
    mode: "cors",
    cache: "no-cache",
    credentials: "same-origin",
    headers: {
      "Content-Type": "application/json",
    },  
    redirect: "follow",
    referrerPolicy: "no-referrer",
    body: JSON.stringify(data),
  }

Request オブジェクト

Request は fetch API のインターフェイスで、リソースのリクエストを表します。

次のよう Request も可能です。

const request = new Request("https://example.com", {
  method: "POST",
  body: '{"foo": "bar"}',
});

const url = request.url;
const method = request.method;
const credentials = request.credentials;
const bodyUsed = request.bodyUsed;

Request オブジェクトを、 fetch() 呼び出しの引数として渡すことで API リクエストを取得でき、かつレスポンスを取得できます。

つまり、次のようにすることができます。

fetch(request)
  .then((response) => {
    if (response.status === 200) {
      return response.json();
    } else {
      throw new Error("Something went wrong on API server!");
    }
  })
  .then((response) => {
    console.debug(response);
    // …
  })
  .catch((error) => {
    console.error(error);
  });

Response オブジェクト

Response オブジェクトから JSON の本体の内容を抽出するには json() メソッドを使用します。これは Response 本体のテキストを JSON として解釈して第2のPromise オブジェクトを返します。

使用例

copy

async function Movies() {
    const response = await fetch("http://hogehoge.com/movies.json");
    const movies = await response.json();
    console.log(movies);
}

then を使ってみる

上のコードをチェーンメソッドで続けて書けるようです。

copy

fetch("http://hogehoge.com/movies.json")
    .then((response) => response.json())
    .then((movies) => console.log(movies));

body: JSON.stringify(data)

init オブジェクトの中でいつも手間がかかるのが JSON.stringify(data) で data が重なってくると大カッコ(ブラケット)、中カッコ(ブレース)、小カッコの開き閉じで混乱することが間々あります。

配列、Mapオブジェクトで組み合わせて少し考えてみたのが次のものです。

JSON文字列への変換1

copy

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <form>
    <input type="button" id="run" value="実行" onclick="test()" />
    <input type="text" id="location" size="10" />
  </form>
  <p id="json" style="width: 900px; word-wrap: break-word;"></p>
  <script language="javascript" type="text/javascript">

    function test() {
      //testデータの作成
      const readdata = [];
      for (let i = 1; i <= 50; i++) {
        let text = "0000000000" + i;
        //"DATA" + 10桁の数字
        readdata.push("DATA" + text.substring(text.length - 10));
      }
console.log(readdata);
      //テキストボックスの値を取得
      const mylocation = document.getElementById("location").value
console.log(mylocation);
      //データ構造の作成
      const midasi = ["field1", "field2", "field3", "field4", "field5",
                      "field6", "field7", "field8", "field9", "field10",
                      "field11", "field12", "field13", "field14", "field15"];

      //一つのMapオブジェクトに15個のデータペアを作成
      const map1 = new Map();
      const map2 = new Map();
      const map3 = new Map();
      const map4 = new Map();
      const map5 = new Map();
      const map6 = new Map();
      const map7 = new Map();
      const map8 = new Map();

      //
      const list = [map1, map2, map3, map4, map5, map6, map7, map8];

      //既定の数値
      const midasilen = midasi.length;
      const readdatalen = readdata.length;
      const totallen = midasilen * list.length;

      //データ構造体の作成
      for (let i = 0; i < totallen; i++){
        //iの属するlistのインデックスを求める
        let j = Math.floor(i / midasilen);
        if (i < readdatalen) {
          //iの属するmidasiのインデックスは i % midasilen であるから
          list[j].set(midasi[i % midasilen] , readdata[i]);
        } else {
          list[j].set(midasi[i % midasilen], null);
        }
      }

      //データ構造体を2つに分ける
      const list1 = [Object.fromEntries(list[0]),
                     Object.fromEntries(list[1]),
                     Object.fromEntries(list[2]),
                     Object.fromEntries(list[3])];
      const list2 = [Object.fromEntries(list[4]),
                     Object.fromEntries(list[5]),
                     Object.fromEntries(list[6]),
                     Object.fromEntries(list[7])]

      const mainmap = new Map();
      mainmap.set("list1", list1);
      mainmap.set("list2", list2);

      document.getElementById("json").innerHTML = JSON.stringify(Object.fromEntries(mainmap));

    }
  </script>
</body>
</html>

JSON文字列への変換2

copy

      //データ構造体の作成
      for (let i = 0; i < totallen; i++) {
        //iの属するlistのインデックスを求める
        let j = Math.floor(i / midasilen);
        if (i < readdatalen) {
          let p = new Map();
          p.set(keylist[0], readdata[i]);
          p.set(keylist[1], mylocation);
          //iの属するmidasiのインデックスは i % midasilen であるから
          list[j].set(midasi[i % midasilen], Object.fromEntries(p));
        } else {
          list[j].set(midasi[i % midasilen], null);
        }
      }

どうやっても私は混乱しそうです。この件はここまでとします。