【JavaScript】XMLHttpRequestとは?使い方から実用例まで初心者向けにわかりやすく解説

はじめに

JavaScriptで外部のサーバーとやりとりをする方法として、XMLHttpRequest はとても有名です。
Ajax通信という言葉を聞いたことがある方も多いかもしれませんが、その根底にはこの機能が使われています。

ネットワークを介してデータをやりとりする仕組みを学ぶと、いきなりハードルが高く感じられることがあるようです。しかし皆さんが目指すのは決して難しい操作をこなすことだけではなく、実際の場面でどのように役立つかを理解することではないでしょうか。

ここでは、初心者の方でもイメージしやすい形でXMLHttpRequest を用いたデータ通信の仕組みや使い方を紹介します。
実務における活用シーンも挙げながら、どういったときに利用されるかを具体的に説明します。

この記事を読むとわかること

  • XMLHttpRequest の基本的な仕組み
  • リクエストの準備からレスポンス処理までの手順
  • 非同期通信を用いた実務シーンでの使用例
  • デバッグやエラー処理のポイント
  • 同じように使われる Fetch API との違い

XMLHttpRequestとは

XMLHttpRequest は、ブラウザとサーバー間でデータをやりとりするためのインターフェースです。
JavaScriptのコードからサーバーにHTTPリクエストを送ることで、ページをリロードすることなく新しい情報を取得できるようになります。

JavaScriptを学び始めたばかりだと、画面の内容を動的に書き換える仕組みをいきなり覚えるのは大変かもしれません。
しかし、非同期通信のメリットを理解しておくと、アプリケーション作成の幅がぐっと広がります。

XMLHttpRequestの基本概念

XMLHttpRequest は名前にXMLが含まれているものの、JSONやテキストなども扱えます。
かつてはAJAXと呼ばれる仕組みの中心として、XML形式のデータを扱うことが一般的でしたが、現在はJSON形式の方がよく使われています。

実際には、以下のような流れでHTTP通信を行います。

  1. XMLHttpRequestオブジェクトを生成する
  2. HTTPリクエストのメソッドやURLを指定する
  3. リクエストヘッダーなどを設定する
  4. リクエストを送信する
  5. レスポンスが返ってきたタイミングで処理を行う

Ajax通信との関係

Ajax は非同期通信手法をまとめて指す言葉であり、必ずしもXMLを使わない場合も含まれます。
ブラウザとサーバー間の通信をリロードなしで行う仕組みがAjaxの中核で、昔は専らXMLHttpRequestが中心でした。

最近は同様の通信を実現するFetch API やサードパーティのライブラリを使うケースも増えました。それでもAjax通信といえば、まずはXMLHttpRequest を想起する方も多いです。

XMLHttpRequestの基本的な使い方

ここでは、実際のコードイメージを通して、XMLHttpRequestをどのように使うのかを見ていきましょう。
初学者の方でもイメージしやすいように、ステップを細かく区切って説明します。

リクエストの準備

まずはXMLHttpRequestオブジェクトの生成と、HTTPメソッドやアクセス先のURLなどを設定します。

// XMLHttpRequestオブジェクトを生成
const xhr = new XMLHttpRequest();

// HTTPメソッドとURLを指定
// ここではGETメソッドを使用して、特定のエンドポイントにアクセスする例です
xhr.open("GET", "https://example.com/api/data");

open メソッドでは、最初の引数にHTTPメソッド、2番目にアクセス先のURLを指定します。
この操作ができた時点ではまだリクエストを送っていないので、リクエスト送信前にヘッダーなどを設定することが可能です。

リクエストの送信

次に、必要に応じてリクエストヘッダーを設定したり、サーバー側に送るボディをセットしたりしてからsend メソッドを呼び出します。

// リクエストヘッダーの設定もここで行える
// 例: JSONデータを送りたい場合
// xhr.setRequestHeader("Content-Type", "application/json");

// リクエスト送信
xhr.send();

もしPOSTメソッドでJSONを送信する場合は、上記のようにsetRequestHeader でヘッダーを設定して、send メソッドにJSON文字列を渡すことでリクエストボディとして送れます。

レスポンスの処理

XMLHttpRequestは、サーバーからのレスポンスを受け取った際にイベントを発火させます。
もっとも代表的なのはonreadystatechange です。

xhr.onreadystatechange = function() {
  // readyState プロパティは、通信状態の変化を表す
  // 4になったとき、通信が完了したことを意味する
  if (xhr.readyState === 4) {
    // ステータスコードが200番台の場合、正常にレスポンスを取得できている
    if (xhr.status >= 200 && xhr.status < 300) {
      // レスポンスの内容は xhr.responseText で参照できる
      const responseData = xhr.responseText;
      console.log("受け取ったデータ:", responseData);
    } else {
      console.log("エラーが発生しました:", xhr.status);
    }
  }
};

readyState の値が4になったら通信完了と判断できるのがポイントです。
xhr.status が200番台であれば成功、それ以外の場合は失敗と見なすのが一般的です。

実務での具体的な使用例

実務では、単にデータを取得するだけでなく、ユーザーが入力した内容をサーバーに送って登録処理を行ったり、画面の一部だけを更新したりします。
ここでは、いくつかのシーンを挙げてみます。

シーン1:ユーザー情報の登録

ユーザーがフォームに入力したデータを、非同期でサーバーに送信し、その結果を画面に反映するようなケースです。
例えば、登録が完了したら成功メッセージを表示し、エラーがあればフォームの下にエラーメッセージを表示します。
このとき、リロードなしで画面をアップデートできると、操作がスムーズに進みますね。

function registerUser(userData) {
  const xhr = new XMLHttpRequest();
  xhr.open("POST", "https://example.com/api/register");

  xhr.setRequestHeader("Content-Type", "application/json");

  xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
      if (xhr.status >= 200 && xhr.status < 300) {
        console.log("登録成功:", xhr.responseText);
        // 画面を更新するコードなどをここに書く
      } else {
        console.log("登録失敗:", xhr.status);
        // エラー表示を行うコードをここに書く
      }
    }
  };

  xhr.send(JSON.stringify(userData));
}

上記のようにPOSTメソッドを使うと、JSON形式のデータをサーバーに送ることができます。
ユーザーの操作に合わせて、素早くデータ登録を行い、その後の画面に反映させると使いやすいアプリケーションになります。

シーン2:リアルタイムなデータ反映

チャットやSNSなどでは、一定時間ごとにサーバーから最新の状態を取得して画面を差し替えることがあります。
このときもXMLHttpRequest を使えば、手軽に定期的なリクエストを発行できます。

function fetchLatestMessages() {
  const xhr = new XMLHttpRequest();
  xhr.open("GET", "https://example.com/api/messages");

  xhr.onreadystatechange = function() {
    if (xhr.readyState === 4 && xhr.status === 200) {
      // メッセージ一覧の取得に成功
      const messages = JSON.parse(xhr.responseText);
      // 取得したメッセージを画面に表示する関数を呼ぶ
      displayMessages(messages);
    }
  };

  xhr.send();
}

// 数秒に一度、最新のメッセージを取得する
setInterval(fetchLatestMessages, 5000);

頻繁にサーバーと通信する場合は、負荷の分散や効率的な設計が必要になります。また、WebSocketや他の仕組みとの比較検討も重要です。

エラー処理やデバッグのポイント

ネットワーク通信を行う以上、エラーや遅延は発生しうるものです。
エラー処理を適切に行うことで、予期せぬ状態を回避し、利用者に安心感を与えることができます。

エラーイベント

onreadystatechange 以外にも、XMLHttpRequestには様々なイベントが存在します。
代表的なものとしてonerror があります。これは、ネットワークエラーやCORSの制限などで通信が失敗したときに呼び出されるものです。

xhr.onerror = function() {
  console.log("ネットワークエラーが発生しました");
};

ステータスコードによる判定

サーバーから返ってくるステータスコードも、エラー処理に活用できます。
404(Not Found)や500(Internal Server Error)が返された場合、ユーザーに対して再送信を促すか、何らかのメッセージを表示するとよいでしょう。

レスポンスを受け取ったとしても、必ずしも正常なデータが返ってくるとは限りません。ステータスコードのチェックや例外的なケースを考慮して、堅牢なエラー処理を組み込みましょう。

同一生成元ポリシーとクロスドメイン通信

ブラウザには 同一生成元ポリシー (Same-Origin Policy) が存在します。
これは、セキュリティ上の理由から、異なるドメインやポートで動作するリソースへのアクセスが制限されるという仕組みです。

CORSを利用したクロスドメイン通信

クロスドメイン通信を行いたい場合、サーバー側で CORS (Cross-Origin Resource Sharing) を設定する必要があります。
具体的には、サーバー側でAccess-Control-Allow-Origin ヘッダーを設定し、許可するオリジンを指定します。

// クロスドメインアクセス可能なURLを指定している例
xhr.open("GET", "https://api.anotherdomain.com/data");

このとき、ブラウザが自動的にサーバーと「本当にアクセスを許可してよいか」を確認する仕組みをPreflightリクエスト と呼びます。
サーバー側が適切に対応していないと、ブラウザでエラーが発生するので注意が必要です。

JSONPの仕組み

かつてはJSONP を使ってクロスドメイン通信を実現する方法もありました。
これは、<script> タグを動的に生成し、サーバーからのレスポンスをJavaScriptのコールバック形式で受け取る手法です。ただしセキュリティ上の懸念から、現在ではCORSを設定する方が一般的です。

イベントハンドリングの活用

すでに触れたように、XMLHttpRequestはonreadystatechange をはじめとする様々なイベントを持っています。
これらをうまく活用すると、通信の各フェーズに応じた振る舞いを実装できます。

readyStateについて

readyState は、以下のような値をとります。

  • 0: 未初期化 (openが呼ばれていない)
  • 1: OPENED (openが呼ばれた)
  • 2: HEADERS_RECEIVED (レスポンスヘッダーを受け取った)
  • 3: LOADING (レスポンスボディを受信中)
  • 4: DONE (通信が完了している)

onabortなどのイベント

ユーザーが通信途中でキャンセルできるようにするなら、xhr.abort() を呼び出す方法があります。
このときには onabort イベントが呼ばれるため、キャンセルをトリガーにしたUI上の動作を定義できます。

通信の進捗をユーザーに見せる場合は、readyStateが3になったタイミングでロード中であることを示す表示などを考えるとよいでしょう。

XMLHttpRequestとFetch APIとの比較

JavaScriptには最近、Fetch API という別の手段が用意されています。
XMLHttpRequestと似たような役割を果たしますが、Promiseベースで動作するため、非同期処理をよりシンプルに扱えると感じる方も多いです。

// Fetch API の使用例
fetch("https://example.com/api/data")
  .then(response => {
    if (!response.ok) {
      throw new Error("レスポンスエラー: " + response.status);
    }
    return response.json();
  })
  .then(data => {
    console.log("受け取ったデータ:", data);
  })
  .catch(error => {
    console.log("エラー:", error);
  });

ただ、XMLHttpRequestの方がレガシーブラウザでの対応が進んでいるなどの事情もあり、すぐにFetch APIへ移行できないプロジェクトもあります。
どちらの方式を採用するかは、プロジェクトの要件や対象とするブラウザの範囲などを踏まえて決定するとよいですね。

セキュリティとパフォーマンスへの意識

非同期通信は非常に便利ですが、データをやりとりするということは、セキュリティ面でのリスクも常に考慮する必要があります。
また、頻繁に通信が発生すると、ネットワーク帯域やサーバーリソースの負荷にも気を配る必要があります。

パフォーマンス面での注意

  • 通信が多すぎると、サーバーやユーザーの回線に負荷をかける
  • 不要なデータまで取得していないかを確認する
  • 一定時間内に複数のリクエストが発生するなら、リクエストをまとめて行う方法を検討する

セキュリティ面での注意

  • HTTPSを使った通信でデータの盗聴を防ぐ
  • サーバーサイドでCORSを適切に設定し、無制限なアクセスを許さない
  • 入力値の検証を徹底し、サニタイズを行う

これらを事前に意識することが、実際の運用で想定外の不具合を防ぐコツです。

XMLHttpRequestを扱う上でのよくある疑問

初心者の方から時折、次のような疑問が挙がります。

同期通信とはどう違う?

XMLHttpRequestには同期モードも存在しますが、画面全体がフリーズしてしまうリスクがあるため、非同期モードでの利用が推奨されます。
同期モードはほぼ使われないと言ってよいでしょう。

レスポンスをJSON以外で処理できるの?

JSON以外にもテキストやXMLなど、様々な形式で受け取れます。実際のデータ形式はresponseType プロパティやレスポンスヘッダーによって決定されます。

const xhr = new XMLHttpRequest();
// バイナリデータを扱いたい場合
xhr.responseType = "arraybuffer";

このように設定すれば、バイナリデータも扱えるようになります。

デバッグを効率化するコツ

慣れていないうちは、非同期通信のデバッグが難しく感じられることがあります。
ブラウザのデベロッパーツールを活用すると、通信のリクエスト・レスポンスをリアルタイムで確認できるため、原因の特定が容易になります。

ネットワークタブの活用

ChromeやFirefoxの開発者ツールにはNetwork タブが用意されています。
送信しているリクエストのURL、ステータスコード、レスポンスボディなどを確認できます。
思わぬエラーで通信が失敗している場合も、ここを見れば解決の手がかりがつかみやすいでしょう。

コンソールログで経過を確認

コード中で適宜console.log を使って、どの段階まで実行されたのかをチェックするのも効果的です。
適度にログを仕込むことで、「どこで想定と違う挙動が起きているのか」を探りやすくなります。

サーバーサイドとの連携

XMLHttpRequestはクライアント側のJavaScriptコードからサーバーへのアクセスを行うので、サーバーサイドの実装とも密接に関わります。
どんなエンドポイントにどのデータを送ればいいのか、どんな形式のレスポンスが返ってくるのかを事前に話し合っておくとスムーズです。

API仕様の事前共有

サーバーサイド担当の方と協力する場合、APIの仕様 を正確に共有しておくことが重要です。
レスポンスの形式が想定と異なる場合、フロントエンドでは正しく解析できずにエラーに繋がります。
また、POSTする内容が不十分な場合は、サーバー側で例外エラーが発生するかもしれません。

RESTやGraphQLなどとの組み合わせ

サーバーがREST風のAPIを提供していることもあれば、GraphQLで提供している場合もあります。
基本的にはURLやリクエストメソッドなどを指定して通信する点は同じですが、送るデータの構造や解析ロジックが少し変わるケースがあります。

継続的な学習のポイント

非同期通信は奥が深く、一度マスターすればとても便利なスキルです。
しかし、常に別の技術的選択肢(たとえばWebSocketなどのリアルタイム通信)と比較する姿勢も大切です。

JavaScriptにはXMLHttpRequest 以外にも多様な通信方法が存在します。
自分が作ろうとしているものや、実務の要件に照らし合わせて最適な方法を選べるようになると、さらに開発の幅が広がるでしょう。

まとめ

ここまで、XMLHttpRequest を利用した非同期通信の基本的な流れや実務での使用例、そしてエラー処理やセキュリティ上の注意点などを解説しました。

  • XMLHttpRequest は、Ajax通信の中心的な機能
  • リクエストの準備(open)、ヘッダー設定(setRequestHeader)、送信(send)の流れを踏む
  • レスポンスの受信後はreadyStatestatus で結果を判定
  • CORS やセキュリティへの配慮が必要
  • Fetch APIWebSocket などの他の手段と比較して、要件に合う手法を選択

非同期通信は、多くのWebサービスやアプリケーションで使われています。
今回の内容を押さえることで、皆さんが取り組むプロジェクトにおいて「どのようにデータをやりとりし、どのようなユーザー体験を提供するのか」を具体的に考えられるようになるでしょう。

JavaScriptをマスターしよう

この記事で学んだJavaScriptの知識をさらに伸ばしませんか?
Udemyには、現場ですぐ使えるスキルを身につけられる実践的な講座が揃っています。