XMLHttpRequest 動作例、レスポンスについて
XMLHttpRequestは、javascriptの言語仕様の更新や、フレームワーク事情から考えると、
利用シーンはかなり少なくなっていますが、ここではひととおりのことを試してみようと思います。
前回の投稿ではクライアント側からサーバ側への通信(主にデータの送信)を試しました。
今回は通信時に、サーバ側から返されるレスポンスについて試してみます。
XMLHttpRequestの通信時には、レスポンスとして次のものが取得できます。
ステータスコード HTTPのステータスを数値形式とテキスト形式で返す レスポンスヘッダ getResponseHeader()、getAllResponseHeaders()で取得 レスポンスボディ responseTextプロパティから取得 テキスト形式、Document形式で取得可能
ざっと書きましたが、具体的な使い方や書き方、動作等はサンプルを下記ながら
試してみようと思います。
XMLHttpRequest 通信時の同期、非同期について
XMLHttpRequestで通信処理する場合、非同期で処理されます。
前回は下記のサンプルを書きました。
let req = new XMLHttpRequest(); let send_string = "test_post_value=123"; req.open("POST", "./receive.php"); req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); req.send(send_string);
この時、最終行にある「req.send」のsendメソッドは、リクエスト送信後にすぐに続きの処理に戻る為、
サーバからの応答を待つことをしません。
ではサーバからの応答を取得するにはどうするのかというと、
XMLHttpRequestオブジェクトのreadystatechangeイベントの発火をきっかけにして処理を行います。
readystatechangeイベントについては、以前の投稿でサンプルを書いて試しています。
また、readystatechangeイベントとセットで覚えておくとよいものに、XMLHttpRequestオブジェクトのreadyStateプロパティがあります。
readyStateプロパティはHTTPリクエストの通信時に状態を表すステータスの値を持ちます。
readyStateプロパティの各ステータスの定数名は以下のようになります。
UNSET OPEND HEADERS_RECEIVED LOADING DONE
定数名に対してのreadyStateの値と意味は以下のようになります。
UNSET 0 open()が呼び出されていない OPEND 1 open()が呼び出された HEADERS_RECEIVED 2 ヘッダを受信 LOADING 3 レスポンスボディの受信中 DONE 4 レスポンスボディの受信完了
通信前、通信中、通信後に readyStateプロパティの値が変化する際、readystatechangeイベントが発火します。
実際の通信時の処理によっては、全てのreadyStateプロパティの値のケースよって、
readystatechangeイベントの発火の順が保証されている訳ではないことに注意が必要です。
サーバ側からの通信完了の目安として、readyStateの値をプロパティ値の値(DONE、4)を判定すると確実な処理ができると言えます。
AJAXでの通信時に、readystatechangeイベントを受信する方法はonreadystatechangeプロパティにイベントハンドラの関数を登録し、
その関数内でリクエスト完了後のサーバ側からのレスポンスを処理します。
レスポンスの処理は主に以下の内容です。
リクエストが完了したかどうか レスポンスのステータスコードをチェック ステータスコードの結果、レスポンスが正しく完了しているかを判定 Content-Typeヘッダを基に、レスポンスの内容をチェック 全てのチェックが問題なければ、レスポンスボディをコールバック関数に渡す
ここまでの内容を確かめる為に、簡単なサンプルを書いてみます。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>テストHTML</title> </head> <body> <input type="button" id="sampleXMLHttpRequest" value="レスポンスのテスト"> <div>通信時の処理内容</div> <div id="ajax_result"></div> <script type="text/javascript"> // ボタン要素のDOMを取得 let element_sampleXMLHttpRequest = document.getElementById('sampleXMLHttpRequest'); // イベントを付与 element_sampleXMLHttpRequest.addEventListener('click', SendXMLHttpRequest, false); // ボタン押下時の処理 function SendXMLHttpRequest() { // 通信処理を画面に出力する為、DOM操作 let result = document.getElementById("ajax_result"); // ajax通信処理 let req = new XMLHttpRequest(); req.open("GET", "./response.php"); req.onreadystatechange = function() { // 検証の為、画面出力とログ出力をする result.innerHTML = 'onreadystatechange start' + '<br />'; result.innerHTML += 'req.readyState -> ' + req.readyState + '<br />'; result.innerHTML += 'req.status -> ' + req.status + '<br />'; console.log('req.readyState -> ' + req.readyState); console.log('req.status -> ' + req.status); // レスポンスの状態 (req.readyState) を判定する if (req.readyState == 4) { result.innerHTML += 'レスポンスは正常 (readyState)' + '<br />'; console.log('レスポンスは正常 (readyState)'); } else { result.innerHTML += 'レスポンスは異常の為、処理終了(readyState) ' + '<br />'; console.log('レスポンスは異常の為、処理終了(readyState) '); return false; } // レスポンスの状態 (req.status) を判定する if (req.status == 200) { result.innerHTML += 'レスポンスは正常 (status)' + '<br />'; console.log('レスポンスは正常 (status)'); } else { result.innerHTML += 'レスポンスは異常の為、処理終了(status) ' + '<br />'; console.log('レスポンスは異常の為、処理終了(status) '); return false; } // getResponseHeaderを取得する let content_type = req.getResponseHeader("Content-Type"); result.innerHTML += 'content_type -> ' + content_type + '<br />'; console.log('content_type -> ' + content_type); // レスポンスの状態 (content_type) を判定する // ここでは検証の為、強制的に「text/html; charset=UTF-8」の文字列で判定する if (content_type == "text/html; charset=UTF-8") { result.innerHTML += 'レスポンスは正常 (content_type)' + '<br />'; console.log('レスポンスは正常 (content_type)'); } else { result.innerHTML += 'レスポンスは異常の為、処理終了(content_type) ' + '<br />'; console.log('レスポンスは異常の為、処理終了(content_type) '); return false; } // responseレスポンス内容を出力 result.innerHTML += 'レスポンス内容の表示 -> ' + req.response + '<br />'; }; req.send(); } </script> </body> </html>
また、サーバ側の response.php は、文字列を出力するだけのサンプルにしています。
<?php echo "response_test_123"; ?>
実際にアクセスして、画面上の「レスポンスのテスト」ボタンを押下すると、
通信時の処理内容 onreadystatechange start req.readyState -> 4 req.status -> 200 レスポンスは正常 (readyState) レスポンスは正常 (status) content_type -> text/html; charset=UTF-8 レスポンスは正常 (content_type) レスポンス内容の表示 -> response_test_123
という通信結果が画面に出力されます。
検証の為、readyState、status、Content-Typeを画面上と、ログ出力をしていますが、
出力された画面と、ログ出力を確認すると、少し違いがあります。
画面への出力は、上記の処理結果が出力されますが、ログには次のように出力されます。
ここでわかることは、画面上のボタンを押下した後、XMLHttpRequestの通信が開始されますが、
ボタンを押下した直後には「req.readyState -> 2」「req.readyState -> 3」等の状態(イベント)が発生していることがわかります。
さきほど「readystatechangeイベントの発火の順が保証されている訳ではない」と書きましたが、
クライアント側からサーバ側へ通信を行う際は、リクエストとレスポンスの状態は保証されていない為、
「レスポンスを確実に受取った」というステータス(ここではreadyStateが4)を判定し、クライアント側の処理を行う必要があることがわかります。
検証の為、画面出力とログ出力をしているので、ソースが長くなりましたが、
詳しく確認する通信状態を細かく考慮する必要があることがわかります。