HTTP通信

アプリケーションが実行できるようになったので、次はGitHubのAPIを呼び出す処理を実装していきます。 当然ですが、GitHubのAPIを呼び出すためにはHTTP通信を行う必要があります。 ウェブブラウザ上でJavaScriptからHTTP通信を行うにはXMLHttpRequestという機能を使います。

XMLHttpRequest

XMLHttpRequestXHR)はクライアントとサーバー間でデータをやり取りするためのAPIです。 XHRを使うことで、ページ全体を再読み込みすることなくURLからデータを取得できます。

GitHubが提供している、ユーザー情報を取得するためのWebAPIを呼び出すコードは次のようになります。 リクエストを送信するためには、まずXMLHttpRequestクラスのインスタンスを作ります。 作成したXHRのインスタンスに、リクエストメソッドとURLを与えることで、HTTPリクエストが組み立てられます。 URLをテンプレート文字列にしているのは、変数でユーザーIDを設定するためです。 URLをオープンして組み立てられたXHRは、最後にsendすることでサーバーとの通信を開始します。

const request = new XMLHttpRequest();
request.open("GET", `https://api.github.com/users/${userId}`);
request.send();

このように、XHRを使ったHTTP通信は基本的に3ステップで行われます。

  1. XHRのインスタンスを生成
  2. URLをオープン
  3. リクエストを送信

レスポンスの受け取り

GitHubのAPIに対してHTTPリクエストを送信しましたが、まだレスポンスを受け取る処理を書いていません。 次はサーバーから返却されたレスポンスのログをコンソールに出力する処理を実装します。

非同期的なXHRの場合、レスポンスはXHRが発火するイベントのコールバック内で受け取れます。 実はXHRにはHTTP通信を同期的に実行するモードも存在しますが、一般的にはXHRを同期的に行うことはありません。 なぜならWebブラウザ上で実行されるJavaScriptはシングルスレッドであり、 HTTP通信を行っている間はすべての処理がブロックされてしまうからです。 そもそも本章のテーマでもあるAJAXの根幹はAsynchronous(非同期的)であることなので、 ここで登場するXHRはすべて非同期的とします。

送信したXHRにHTTPレスポンスが返却されると、loadイベントが発火します。 コールバック関数の第1引数には Eventオブジェクトが渡されます。 このオブジェクトのtargetプロパティには、[イベントを発生させたオブジェクト][](EventTarget)である XHRのインスタンスがセットされています。 XHRのresponseTextプロパティからは、HTTPレスポンスが文字列で取得できます。 また、statusプロパティからはHTTPレスポンスのステータスコードが取得できます。

const request = new XMLHttpRequest();
request.open("GET", `https://api.github.com/users/${userId}`);
request.addEventListener("load", (event) => {
    console.log(event.target.status); // => 200
    console.log(event.target.responseText); // => "{...}"
});
request.send();

HTTPレスポンスの内容によらず、responseTextは常に文字列としてレスポンスを読み取れます。 一方、XHRにはresponseというプロパティもあります。 リクエストを送るときにresponseTypeを指定しておくと、その型にしたがって変換されたオブジェクトがresponseから取得できます。 responseプロパティは便利なAPIなのですが、サーバー側の実装やブラウザの実装などに依存するため、本章では扱いません。 興味があれば、詳細についてはXMLHttpRequest.responseを参照してください。

エラーハンドリング

HTTP通信にはエラーがつきものです。 もちろんXHRを使った通信においても、エラーをハンドリングする方法があります。 XHRではレスポンスの受け取りと同じように、イベントリスナによってエラーのハンドリングが可能です。 サーバーとの通信に際してエラーが発生した場合は、errorイベントが発火されます。

request.addEventListener("error", () => {
    console.log("Network Error!");
});

注意すべき点は、サーバーがレスポンスとして返却するエラーは、通常のレスポンスと同様にloadイベントで受け取ることです。 たとえば、ステータスコードが200以外である場合のハンドリングは次のように行います。

request.addEventListener("load", (event) => {
    if (event.target.status !== 200) {
        console.log(`Error: ${event.target.status}`);
        return;
    }
});

ここまでの内容をまとめ、GitHubからユーザー情報を取得するの関数をgetUserInfoという名前で定義します。

function getUserInfo(userId) {
    const request = new XMLHttpRequest();
    request.open("GET", `https://api.github.com/users/${userId}`);
    request.addEventListener("load", (event) => {
        if (event.target.status !== 200) {
            console.log(`${event.target.status}: ${event.target.statusText}`);
            return;
        }
        console.log(event.target.status);
        console.log(event.target.responseText);
    });
    request.addEventListener("error", () => {
        console.error("Network Error");
    });
    request.send();
}

index.jsでは関数を定義しているだけで、呼び出しは行っていません。 ページを読み込むたびにGitHubのAPIを呼び出してしまうと、呼び出し回数の制限を超えるてしまうおそれがあります。 そこでgetUserInfo関数を呼び出すため、HTMLドキュメント側にボタンを追加します。 ボタンのclickイベントでgetUserInfo関数を呼び出し、固定のユーザーIDを引数として与えています。

<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Ajax Example</title>
    </head>
    <body>
        <h2>GitHub User Info</h2>

        <button onclick="getUserInfo('js-primer-example');">Get user info</button>
        <script src="index.js"></script>
    </body>
</html>

準備ができたら、ローカルサーバーを立ち上げてindex.htmlにアクセスしましょう。 ボタンを押すとHTTP通信が行われ、コンソールにステータスコードとレスポンスのログが出力されます。

XHRで取得したデータの表示

また、開発者ツールのネットワーク画面を開くと、確かにGitHubのサーバーに対してHTTP通信が行われていることを確認できます。

開発者ツールでHTTP通信の記録を確認する

results matching ""

    No results matching ""