Ajax (Asynchronous JavaScript + XML) で、サーバから非同期にレスポンスを受け取る方法について解説します。
ここで解説する方法では、jQueryなどのライブラリを必要としません。
| 分類 | プロパティ※1 | 型 | 説明 |
|---|---|---|---|
| readyState | short | HTTPリクエストの状態 | |
| レスポンス ※2 | responseText | String | レスポンス (ヘッダを含まない) |
| responseXML | Document | レスポンス (XMLとして解析済み) | |
| responseType | String | 期待するレスポンスの型 | |
| response | varies | レスポンス (responseTypeの型で解析済み) | |
| ステータス | status | short | HTTPステータス コード |
| statusText | String | HTTPステータス コードの文字列表現 |
| 値 | 名前 | 説明 | 発生タイミング |
|---|---|---|---|
| 0 | Uninitialized | 初期状態 | 次のいずれか
|
| 1 | Open | リクエスト未送信 | open()メソッドの呼び出し後 |
| 2 | Sent | レスポンス未受信 | send()メソッドの呼び出し後 |
| 3 | Receiving | レスポンス受信中 | サーバからデータを受信中※1 |
| 4 | Loaded | レスポンス受信済み | サーバのレスポンスの受信が完了 |
| メソッド | 説明 |
|---|---|
| open() | HTTPリクエスト パラメータを初期化する |
| send() | HTTPリクエストを送信する |
| abort() | リクエストを停止する |
| getAllResponseHeaders() | すべてのHTTPレスポンス ヘッダを未解析の文字列として取得する |
| getResponseHeader() | 指定のHTTPレスポンス ヘッダの値を取得する |
| setRequestHeader() | 未送信のリクエストに、HTTPリクエスト ヘッダを設定する |
| イベントハンドラ | 説明 |
|---|---|
| onreadystatechange | readyStateプロパティが変更されるごとに呼び出される |
XMLHttpRequestオブジェクトの生成は、Internet Explorer 7より前とそれ以外のブラウザで異なります。よって複数の方法で試行する必要があります。
IE7より前のサポートを考慮しないならば、new XMLHttpRequest()だけで十分です。
// XMLHttpRequestオブジェクトを生成する var xmlHttpRequest; try { xmlHttpRequest = new XMLHttpRequest(); } catch( e ) { try { // IE6向けの処理 xmlHttpRequest = new ActiveXObject( 'Msxml2.XMLHTTP' ); } catch( e ) { // IE5とIE5.5向けの処理 xmlHttpRequest = new ActiveXObject( 'Microsoft.XMLHTTP' ); } }
// 非同期の通知を受け取るイベントハンドラを登録する xmlHttpRequest.onreadystatechange = function() { var READYSTATE_COMPLETED = 4; // readyStateの値 if( this.readyState == READYSTATE_COMPLETED ) { var HTTP_STATUS_OK = 200; // HTTPステータス コード if( this.status == HTTP_STATUS_OK ) { // サーバから受信したレスポンスを表示する alert( this.responseText ); } else { // エラーを表示する alert( this.status + this.statusText ); } } }
※リクエストの成否に無関心で、レスポンスを受け取る必要もなければ、このようにreadystatechangeイベントに反応する必要はありません。
受信に失敗するときには、XmlHttpRequest.statusを確認します。
readyStateが4を返すのを確認せず、レスポンスの受信完了の前にレスポンスを読み込もうとすると、InvalidStateError例外が発生します。
リクエストの詳細を設定し、そしてリクエストを送信します。
// HTTPリクエストパラメータを初期化する xmlHttpRequest.open( 'GET', requestUrl ); // HTTPリクエストを送信する xmlHttpRequest.send( null );
void open(
String method, // HTTPメソッド (GET, POST, HEAD, etc...)
String url, // リクエスト対象のURL
boolean async, // 非同期に処理するかどうか (省略もしくはtrueで非同期)
String username, // 認証情報 (ユーザー名)
String password // 認証情報 (パスワード)
)
open() - XMLHttpRequest - Web API Interfaces | MDN
メソッド名はopenとなっていますが、このメソッドは接続を開いたりはしません。send()メソッドを実行するためのリクエストを初期化するだけです。既定では非同期ですが、第3引数のasyncをfalseとすることで同期通信とすることも可能です。
GETでリクエストするならばmethodを'GET'として、クエリをurlの末尾に付加します。
リクエスト対象とできるURLには、以下の制限があります。
しかしJSONPでデータを受け取れるならば、スクリプトを動的に読み込む方法で同一起源ポリシーの制約を回避できます。
void send(
Object body // リクエストの本体
)
send() - XMLHttpRequest - Web API Interfaces | MDN
POSTでリクエストする場合には、body引数でリクエスト本体を指定します。それ以外のメソッドではnullを指定します。
同一のレスポンスが予期せずくり返されるならば、それはキャッシュの影響かも知れません。そのような場合にはキャッシュが無効化されるようにリクエストします。
データの種類によっては、それを解析した結果として受け取ることができます。
レスポンスがテキストならば、responseTextプロパティからString型で取得できます。
レスポンスがXMLならば、responseXMLからDocument型で取得できます。ただしXMLとして解析されるためには、次の条件を満足する必要があります。
この条件を満たさない場合、このresponseXMLはnullを返します。
XML以外のデータについては、responseTypeで型を指定することで、その型として解析された結果をresponseから取得できます。
| 値 | responseプロパティのデータ型 |
|---|---|
| '' | String |
| 'arraybuffer' | ArrayBuffer |
| 'blob' | Blob |
| 'document' | Document |
| 'json' | Object |
| 'text' | String |
ただしresponseTypeで指定可能な値はブラウザによってサポートが異なり、サポートされないブラウザでは指定の型で取得できません。Browser compatibility - XMLHttpRequest - Web API Interfaces | MDN
その場合でもJSONについては、responseTextプロパティをeval()に渡すことで、JavaScriptのオブジェクトとして取得することが可能です。なおレスポンスが指定したresponseTypeに合致しない場合には、responseはnull、responseTextはundefinedを返します。
レスポンスとしてJSONを期待する場合を考えます。
var xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.onreadystatechange = function()
{
if( this.readyState == 4 && this.status == 200 )
{
if( this.response )
{
// JSONとして取得できたならばresponseはObject型であるため、
// そのプロパティのようにレスポンスのデータにアクセスできます。
alert( this.response.prop );
}
}
}
xmlHttpRequest.open( 'GET', 'sample.json', true );
xmlHttpRequest.responseType = 'json';
xmlHttpRequest.send( null );