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のインスタンスを生成(new XMLHttpRequest
  2. リクエストを初期化(request.openメソッド)
  3. リクエストを送信(request.sendメソッド)

レスポンスの受け取り

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

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

送信したXHRにHTTPレスポンスが返却されると、loadイベントが発生します。 XHRインスタンスのaddEventListenerメソッドにイベント名とコールバック関数を渡すことで、指定したイベントを受け取れます。 コールバック関数の第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.error("Network Error!");
});

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

request.addEventListener("load", (event) => {
    if (event.target.status !== 200) {
        console.error(`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.error(`${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="ja">
  <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通信の記録を確認する