Fetch API
ブラウザで動くJavaScriptからHTTPリクエストを発行する
これまで、ブラウザがサーバーに対してリクエストを送信するのは、リンクがクリックされたときや、フォームが送信されたときなど、ページの再読み込みが起こる場合のみでした。
ブラウザ上で動くJavaScriptから利用できるFetch APIを用いると、任意のタイミングでリクエストが発行できるようになります。
サーバーとクライアント、どちらで動くJavaScriptなのかに注意しながら、次のプログラムを実行してみましょう。
<button id="fetch-button">天気予報を見る</button>
document.getElementById("fetch-button").onclick = async () => {
const response = await fetch("/weather");
const weather = await response.text();
alert(weather);
};
async () => {}は、非同期関数、つまりasyncキーワードのついた関数を生成するためのアロー関数式です。
fetch関数は、リクエストを発行するための関数です。標準ではGETリクエストが発行されます。この関数の戻り値にawait 演算子を適用すると、発行したリクエストに対するResponseクラスのインスタンスが得られます。fetch関数を利用することで、ページの再読み込みを伴わず、関数が実行されるタイミングでリクエストを発行することができます。
Response#textメソッドは、レスポンスボディ全体を文字列として読み込むための非同期関数です。
なお、サーバーでは次のプログラムが動作しているものとします。
import express from "express";
const app = express();
app.use(express.static("./public"));
app.get("/weather", (request, response) => {
response.send("晴れ");
});
app.listen(3000);
POSTリクエストを送信する
何もオプションをつけずに呼び出されたfetch関数は、GETリクエストを送信します。しかしながら、fetch関数の第2引数に指定したオブジェクトのmethodプロパティに"post"を指定することで、POSTリクエストを送信できます。
このとき、リクエストボディは、fetch関数の第2引数に指定したオブジェクトのbodyプロパティに指定します。
document.getElementById("send-button").onclick = async () => {
const name = document.getElementById("name").value;
const age = document.getElementById("age").value;
const body = new URLSearchParams({ name: name, age: age });
const response = await fetch("/send", { method: "post", body: body });
const text = await response.text();
alert(text);
};
import express from "express";
const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.static("./public"));
app.post("/send", (request, response) => {
response.send(
`あなたの名前は ${request.body.name}で、${request.body.age}歳ですね。`,
);
});
app.listen(3000);
HTMLのフォームで送ったものと同じ形式でデータを送信するには、GETリクエストとPOSTリクエスト節で扱ったように、リクエストボディがクエリ文字列の形式になっている必要があります。URLSearchParamsクラスを用いると、クエリ文字列を簡単に扱うことができます。この例では、リクエストボディにはname=入力された名前&age=入力された年齢といった文字列が格納されます。
リクエストボディにJSONを使用する
前項では、リクエストボディにクエリ文字列の形式を用いましたが、JSONを用いることで、より複雑なデータを扱えるようになります。
JSON.stringify関数は、JavaScriptオブジェクトを受け取ってJSON文字列を返す関数です。この値をリクエストボディに指定しています。
fetch関数の第2引数のheadersオプションでは、リクエストヘッダを指定します。リクエストボディにJSONを指定する場合は、Content-Typeリクエストヘッダを"application/json"に指定します。
document.getElementById("send-button").onclick = async () => {
const name = document.getElementById("name").value;
const age = document.getElementById("age").value;
const json = JSON.stringify({ name: name, age: age });
const response = await fetch("/send", {
method: "post",
headers: { "Content-Type": "application/json" },
body: json,
});
const text = await response.text();
alert(text);
};
サーバー側では、リクエストボディのJSONを解釈するため、express.urlencodedの代わりにexpress.jsonを用います。
import express from "express";
const app = express();
app.use(express.json());
app.use(express.static("./public"));
app.post("/send", (request, response) => {
response.send(
`あなたの名前は ${request.body.name}で、${request.body.age}歳ですね。`,
);
});
app.listen(3000);
Content-Typeリクエスト・レスポンスヘッダContent-Typeヘッダは、リクエストボディやレスポンスボディの種類を識別するために使用されます。ここで使用する種類は、MIMEタイプと呼ばれます。
代表的なMIMEタイプとして、次のような値が定義されています。
| MIME タイプ | 種類 |
|---|---|
text/html | HTML |
text/css | CSS |
text/javascript | JavaScript |
application/json | JSON |
image/jpg | JPEG |
image/png | PNG |
演習問題
Fetch APIを用いてチャットアプリを作成してみましょう。
ヒント
掲示板を作ったときと同じく、messagesという配列をサーバー側に用意し、メッセージが送信されたらその配列に要素を追加するようにしましょう。
const messages = [];
app.post("/send", (request, response) => {
// メッセージを追加
});
/messagesへのGETリクエストに対し、メッセージの一覧をJSONで応答するようにしてみましょう。
express.Response#jsonメソッドは、受け取ったオブジェクトをJSON.stringifyによってJSONとしたうえでレスポンスするためのメソッドです。このとき、Content-Typeレスポンスヘッダは自動的に"application/json"に設定されます。
app.get("/messages", (request, response) => {
response.json(messages);
});
新着メッセージを確認するために、定期的に/messagesに対してfetch関数を用いてリクエストしましょう。setInterval関数が利用できます。
setInterval(async () => {
const response = await fetch("/messages");
// レスポンスを処理する
}, 1000);
innerHTMLプロパティを空文字列とすることで要素の子要素を全て削除できます。document.createElement関数を用いて再び生成し直しましょう。
<ul id="message-list"></ul>
const messageList = document.getElementById("message-list");
messageList.innerHTML = "";
for (const message of messages) {
const li = document.createElement("li");
li.textContent = message;
messageList.appendChild(li);
}
解答
解答は次のリンクを参照してください。