これでできる! クロスブラウザJavaScript入門

第12回XMLHttpRequest入門

こんにちは、太田です。前回はJSONPについて解説しました。今回は、XMLHttpRequestについて解説していきます。

XMLHttpRequestとは

XMLHttpRequestはブラウザ上でサーバーとHTTP通信を行うためのAPIです。

名前にXMLが付いていますがXMLに限ったものではなく、HTTPリクエストを投げてテキスト形式かDOMノードでレスポンスを受け取る機能を持っています。

仕様としてはW3CよりXMLHttpRequestとして定義されており、2010年8月3日にCandidate Recommendation(勧告候補)となったばかりです。また、XMLHttpRequest Level 2の策定も進められています。

XMLHttpRequestの機能と特徴

前回のJSONPと比べると機能的には大きな違いはありません。ただ、スキーム、ドメイン、ポート(これをまとめてオリジンと呼びます)すべてが一致するしたURLとしか通信できない点がJSONPとの決定的な違いです。この制限により、⁠比較的)安全にサーバーと通信することができます。

XMLHttpRequestの簡単な歴史

XMLHttpRequestは元々はIE 5において、ActiveXObjectのMicrosoft.XMLHTTPコンポーネントとして実装されたのが始まりです。詳しくはAlex Hopmann's Web SiteのThe story of XMLHTTP日本語訳で語られているように、名前にXMLが入ったのは単に製品の出荷に間に合わせるための都合だったと書かれています。

Mozillaがすぐさま追従してXMLHttpRequestを実装し、Opera、SafariもMozillaの実装に従いました。さらにIE 7もMozillaの実装にあわせて改めてXMLHttpRequestを実装したため、XMLHttpRequestは実質的な標準APIとなりました。またその動きに合わせてW3Cでの標準化も進められ、2010年8月3日に勧告候補にまで至りました。

XMLHttpRequestの使い方

まずはXMLHttpRequestの簡単なサンプルを見てみましょう。次のコードは現在みているページをXMLHttpRequestで取得して、テキストエリアにソースを表示するサンプルです。

XMLHttpRequestのサンプル(IE 6以外)
var btn1 = document.getElementById('xhr-btn1');
btn1.onclick = function(){

  var xhr = new XMLHttpRequest();
  xhr.open('GET', location.href, true);
  xhr.onreadystatechange = function(){
    // 本番用
    if (xhr.readyState === 4 && xhr.status === 200){
      var result1 = document.getElementById('xhr-result1');
      result1.value = xhr.responseText;
    }
    // ローカルファイル用
    if (xhr.readyState === 4 && xhr.status === 0){
      var result1 = document.getElementById('xhr-result1');
      result1.value = xhr.responseText;
    }
  };
  xhr.send(null);
};

このようにXMLHttpRequestの基本的な使い方は、次の4ステップからなります。

  • new XMLHttpRequestでオブジェクトを作る
  • openメソッドで送信時のメソッド、送信先URL、非同期通信か同期通信か真偽値で設定
  • onreadystatechangeで読み込み時の動作を指定
  • sendメソッドでリクエストを送信、引数はGETの場合nullを、POSTの場合はデータを&で繋げた文字列にして渡す。

openメソッドの第3引数で同期通信にすることも可能ですが、同期通信はsendしてからサーバーのレスポンスがあるまでJavaScriptの処理をすべて止めてしまうため、ユーザーを待たせてしまいます。省略時は非同期通信になるので、省略してもかまいません。

なお、呼び出す順番はこの順番を崩さないようにしましょう。まずopenしてからプロパティなどを編集し、最後にsendを実行する形が最も確実に動作します。

レスポンスの処理

onreadystatechangeで「読み込み途中⁠⁠、および「読み込み完了⁠⁠・⁠失敗」時の処理を行います。

まず、読み込みの状態を管理するのはreadyStateプロパティです。readyStateが4の時、読み込みの完了を示します。

そして、statusプロパティによってその読み込みが成功したのか、失敗したのかといったことがわかります。成功時のステータスコードは通常200なので、サーバー側で特殊な処理をしていない限りは200かどうかをチェックするだけで十分です。

読み込みが完了していてステータスが200なら、responseTextからレスポンスの内容を読み取ることができるでしょう。

レスポンスとしてはresponseXMLというプロパティもありますが、こちらは名前の通りXMLを受け取ったときにしかその値を読み取ることができません。具体的にはレスポンスヘッダのContent-Typeにxmlが含まれていて、XMLとしてパース可能である必要があります。

JSONを受け取った場合

XMLHttpRequestのレスポンスとしてJSONを受け取った場合、それはresponseTextのデータとして読み取ることになります。つまり、それはJavaScriptのオブジェクトではありません。できれば直接JSONをオブジェクトとして読み取りたいところですが、今のところそういったAPIは用意されていません。従ってJSONをパースしてオブジェクトに変換する必要があります。

JSONのパースに対応しているのは各ブラウザの比較的新しいバージョンに限られます。そのため、手軽にJSONをJavaScriptに変換できるevalなどがよく使われてしまっています。しかし、それらは本来のJSON形式でなくとも解釈できてしまうため、安全とはいえません(もちろんJSONPにくらべれば格段に安全ではありますが⁠⁠。そこでjson.orgで公開されているjson2.jsを用いるのが良い選択です。なお、json2.jsの1行目にはalertが入っています。これはjson2.jsを直接参照されないようにするための予防策です。

json2.jsは読み込むだけでJSON.parseメソッドが利用可能になります。

JSON.parseのサンプル
  var data = JSON.parse(xhr.responseText);

IE 6対応

IE 7からXMLHttpRequestに対応したと書きましたが、つまりIE 6にはXMLHttpRequestがありません。その代わりに最初に出てきたActiveXのXMLHTTPを用います。なおXMLHTTPにはいくつかのバージョンがあり、なるべく新しいバージョンを使えるように次のようにXMLHttpRequestを定義するとよいでしょう。

XMLHttpRequestのサンプル(IE 6含む)
if (!window.XMLHttpRequest){
  XMLHttpRequest = function () {
    try {
      return new ActiveXObject("Msxml2.XMLHTTP.6.0");
    } catch (e) {}
    try {
      return new ActiveXObject("Msxml2.XMLHTTP.3.0");
    } catch (e) {}
    try {
      return new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {}
    throw new Error("This browser does not support XMLHttpRequest.");
  };
}
var btn2 = document.getElementById('xhr-btn2');
btn2.onclick = function(){
  var xhr = new XMLHttpRequest();
  xhr.open('GET', location.href, true);
  xhr.onreadystatechange = function(){
    if (xhr.readyState === 4 && xhr.status === 200){
      var result2 = document.getElementById('xhr-result2');
      result2.value = xhr.responseText;
    }
    if (xhr.readyState === 4 && xhr.status === 0){
      var result2 = document.getElementById('xhr-result2');
      result2.value = xhr.responseText;
    }
  };
  xhr.send(null);
};

XMLHttpRequestオブジェクトの作り方以外はIE 6もほぼ同様に扱うことができます。

XMLHttpRequest Level 2

XMLHttpRequest Level 2についても簡単に触れておきます。XMLHttpRequest Level 2は従来のXMLHttpRequestをより使いやすく・高機能にしたAPIです。

  • onreadystatechangeの代わりにonloadを使用できる
  • クロスオリジン通信が可能になった
  • アップロード・ダウンロード時の進捗をイベントとして把握することができるようになった
  • overrideMimeTypeによってMIMEタイプ・文字コードを制御可能になった
  • バイナリを受信可能になった

Level 2で定義されたonload、onerrorはリクエストの成功時、失敗時のイベントを処理することができます。onreadystatechangeではreadyStateとstatusをチェックしていた部分が省略できるため、かなり簡潔にXMLHttpRequestの処理を記述できるようになっています。

クロスオリジン通信については、サーバー側でCross-Origin Resource Sharingに従ったアクセスコントロールを行うことで特定のオリジンからのXMLHttpRequestにレスポンスを返すことができるようになりました。

従来はオリジンの異なるURLに対してはsendメソッドを呼んだ段階でエラーになりリクエスト自体が発生しないという動作でしたが、クロスオリジン対応のブラウザではリクエストは正常に行われ、アクセスコントロール次第でonloadが呼ばれるか、onerrorが呼ばれるかが変わるという動作に変わりました。

つまり、GET以外のメソッドをクロスドメインで発生させることが容易になっているため、CSRF(クロスサイトリクエストフォージェリ)などに今まで以上に注意が必要となっています(従来も不可能だったわけではないので、脅威の度合いはそれほど変わっていないと思われます⁠⁠。

XMLHttpRequestの実装状況

最後に、XMLHttpRequestの各メソッド、プロパティについて、それぞれのブラウザの対応状況をまとめておきます。まずはFirefoxからChrome、Safari、Operaの実装状況です。Opera以外はXMLHttpRequest level 2までほぼ実装が進んでいます。

メソッド/プロパティLevelFirefoxChromeSafariOpera
abort1
getAllResponseHeaders1
getResponseHeader1
open1
send1
setRequestHeader1
onreadystatechange1
readyState1
responseText1
responseXML1
status1
statusText1
withCredentials2
upload2
responseBody2
onloadstart2
onprogress2
onabort2
onerror2
onload2
onloadend2
overrideMimeType2

続いて、IEの実装状況です。IE 7はIE 8のXMLHttpRequestとほぼ同じなので省略しています。IE 8はXMLHttpRequest level 2相当のAPIとしてXDomainRequestという独自APIを用意しています。このXDomainRequestはかなりクセが強く、使用できるAPIが限られています(おそらくは、クロスドメインでレスポンスヘッダを取得できることが問題視されたのではないかと思われますが、詳細は不明です⁠⁠。

メソッド/プロパティLevelIE 6 (XMLHTTP)IE 8 (XMLHttp Request)IE 8 (XDomain Request)
abort1
getAllResponseHeaders1
getResponseHeader1
open1
send1
setRequestHeader1
onreadystatechange1
readyState1
responseText1
responseXML1
status1
statusText1
withCredentials2
upload2
responseBody2
onloadstart2
onprogress2
onabort2
onerror2
onload2
onloadend2
overrideMimeType2
contentTypeIE only
timeoutIE only(IE 8から)
ontimeoutIE only(IE 8から)

まとめ

今回はXMLHttpRequestの基礎を解説しました。XMLHttpRequestは高度なアプリケーションを作る上では欠かせない技術です。次回からはXMLHttpRequestの使い方の具体例を見ていきたいと思います。

おすすめ記事

記事・ニュース一覧