JavaScript/XMLHttpRequest

出典: フリー教科書『ウィキブックス(Wikibooks)』
ナビゲーションに移動 検索に移動
Wikipedia
ウィキペディアXMLHttpRequestの記事があります。

XMLHttpRequest (XHR) とは、ウェブサーバと通信を行うためのJavaScriptのオブジェクトです。

Ajax[編集]

CGIなどの仕組みを使ってサーバからデータを取得するにはウェブページを遷移する必要がありますが、XMLHttpRequestを用いるとページの遷移なしに、動的かつ非同期にデータを取得することができます。このような技術はAjax (asynchronous JavaScript and XML) と呼ばれます。Ajaxを駆使するとJavaScriptを用いてGoogle マップのようなインタラクティブなウェブアプリケーションを構築することができます。

XMLHttpRequestはその名に反してXMLドキュメントでないデータもやり取りすることができます。また、HTTP以外にFTP通信も行うことができます[1](ローカルホストではローカルファイルも取得可能)。ただし、XMLHttpRequestを用いて通信できるのは同じドメインのサーバに限られるため、外部のサーバと通信をすることはできません。これを同一生成元ポリシー(どういつせいせいもとポリシー、same origin policy)といいます。

XMLHttpRequest[編集]

サンプルコードおよび前提の解説[編集]

まず、これらの機能は、webサーバを仲介して行う必要がある。よってコードをテストするには、まずwebサーバ(ApacheやXAMPPなど)を立ち上げて、そこのドキュメントルートで行う必要がある。ドキュメントルートの説明については、『PHP/確実に動作させるまで』に説明しておいたので、webサーバに詳しくなければ参照してもらいたい。(もしwebサーバの使い方が全く分からない場合、JavaScriptの学習はいったん中断して、上記リンク先のPHPの単元を先に学習したほうが良いだろう。)

とりあえず、サーバの設定や構成の準備がうまく行ってるかどうかを、次のコードで試してみよう。下記プログラムは、XMLHttpRequestの動作確認用のプログラムである。(本来なら動作確認のほかに、サーバとのデータ送受信などを行いたいのだが、コードが長くなるので、今回はまだ動作確認だけにしている。)

ただし、アクセス先のテキストファイルとして aisatu.txt を用意してもらいたい(あいさつテキストの意味)。今後の作業のため、aisatu.txt には「hello world」とでも書いておく。(単にテキストファイルや画像を読み込むだけなら HTML の通常の機能でも可能だが、今回は動作確認なので、そのような単純な事例にしている。なお、実用の場合には サーバのPHPプログラム(rubyなど別言語でも構わない)と、もっと複雑な通信をしたりするだろう。)

さて、ともかく動作確認のために下記コードを test.html という名前で保存してほしい。

そして、test.htmlをwebサーバのドキュメントルートにアップロードしておくこと。

それができたら、下記コードを、webサーバを介してwebブラウザでアクセスしよう。

なお、下記コードで alert させているのは、通信途中の動作確認もしたいためである。

コード test.html (これは htmlファイル)
<script>
  var flag=0; // デバッグ用に使った残骸

  var xhr = new XMLHttpRequest();
  xhr.responseType = 'text' ;

  xhr.onreadystatechange = function(){
    
    if (xhr.status === 0){    
      //document.write("0 toutatu<br>"); // 0に到達
    }
        
    if (xhr.status === 200){    
      document.write("200 toutatu<br>");
    }
    
    if (xhr.status === 404){    
      document.write("404 status<br>");
    }
    
    if (xhr.readyState === 0 ){    
      document.write("read is 0<br>");
    }
    if (xhr.readyState === 1 ){    
      document.write("read is 1<br>");
      flag =1;
       alert("test1");
    }
   if (xhr.readyState === 2 ){    
      document.write("read is 2<br>");
      flag =2;
      alert("head2");
    }
    if (xhr.readyState === 3 ){    
      document.write("read is 3<br>");
      flag =3;
      //alert("Load3");
      
      
    }
    if (xhr.readyState === 4 ){    
      document.write("read is 4.    Done!<br>");
      document.write("status is " + xhr.status  + " !  ");
     //  alert("DDD4");   // デバッグ用
    }
    
    if (xhr.readyState === 4 && xhr.status === 0 ){    
      alert("fault."); // 失敗
    }
    
    
    if (xhr.readyState === 4 && xhr.status === 200 ){    
      alert("Great! reach4 and State 200 tassei.");// 4に到達。 200 state を達成
      document.write("<br>" + xhr.responseText + "<br>");
    }
    
   // document.write("uytre"); // デバッグ用    
    
  }  ;
  
  //xhr.open('GET', 'http://localhost/aisatu.txt', true); // 下でうまくいかない場合はこれで試す
 xhr.open('GET', '/aisatu.txt');
  //xhr.open('GET', 'husei'); // 意図的に失敗したい場合に使う。
  

  xhr.send(null);
    
</script>

<body>
jjf // html読み込みをしてるかどうかの確認に使うための、無意味な文字列
</body>
※ なお、上コード中の「xhr」とは別に予約語ではなく、単なる変数名である。なので、「xhr」でなくとも他の変数名でも構わない。

さて、このコードに、webサーバを介してwebブラウザでアクセスしよう(再掲)。

もし、test.htmlというファイル名でローカルホストにアップロードしたなら、 http://localhost/test.html というアドレスにブラウザでアクセスすることで、結果を見れる。

表示結果

動作に成功している場合、alertボックスを何回かクリックして閉じたあと、最終的に下記のように表示される(なお alert の用途はここでは通信途中の確認用)。

200 toutatu
read is 2
200 toutatu
read is 3
200 toutatu
read is 4. Done!
status is 200 ! 
Hello World! 

この「200」や「4」は、JavaScriptの規格で意味が決まっている数字である(技術的な解説については、のちのセクションで後述する)。

その規格の意味により、とりあえず上記の動作確認では「200」や「4」と表示されれば、成功である。

規格について抜粋的に説明すると、readyStateプロパティが4なら、すべての応答データを取得できている[2]という意味である。

なので、とりあえず、成功なら 4 が表示されているハズである。

また、「200」と言うのは直前のリクエストに対して「成功」という意味である。失敗なら、200以外のメッセージであり、たとえば「0」や「404」(リソースが存在しない場合)などである。

コードの技術的な解説については、のちのセクションで解説する。

最後の「Hello World! 」は、単にaisatu.txt から送られてきたデータを表示しているだけである。なので、もし aisatu.txt に別の文字列を書いておけば、それが表示される。(Fedora Linux 35 で実験した限りでは、aisatu.txt に書かれた文字列が日本語の文字列でも、正しく表示される。)


ミスの原因[編集]

通信に成功せずに表示結果が上記の成功例と異なる場合、なんらかの失敗をしている。

よくあるミス

・ダブルクリック起動してしまっているミス
失敗する場合、原因は色々とありうるが、特にあるミスとして、 http://localhost/test.html のアクセスではなく、htmlファイルを直接ダブルクリックして起動してしまっていないかを注意してみよう。

単にドキュメントルートの中にhtmlを入れて、そのhtmlファイルをダブルクリック起動しても、失敗する。

必ず、ドキュメントルートにhtmlファイルを入れた上で、さらに必ず http://localhost/test.html にアクセスしているかを確認しよう。

・サーバ立ち上げていないミス
また、そもそもwebサーバが立ち上がっていない場合も当然ながら上記コードの動作確認には失敗するので、ローカルホスト自体http://localhostにもアクセスしてみて、確認してみよう。サーバが立ち上がっているなら、webサーバ(XAMPPまたはApacheなど)の用意している画面(ロゴマークなどがある画面)が表示されるハズであるので、その画面が表示されるか確認してみよう。

技術的な解説[編集]

HTTPコマンド[編集]

「GET」とは、HTTPコマンドの一種です。HTTPコマンドには、主にデータ取得を目的とするために通信する「GET」と、データ送信のために通信する「POST」という、2種類があります。

なお、GETもPOSTもどちらとも、相手先サーバとの通信のために、送信をしています。このため、原理的には「GET」で送信をすることも可能です[3]。もっとも、なるべく本来の目的にあったHTTPコマンドを選んで使うほうが良いプログラムではあるでしょう。

また、GETコマンドは送信URLの末尾にデータを付加して

?キー名=値&

の形式でデータ送信できるのですが、しかし日本語などの2バイト文字や「&」や「?」を正しく扱えません。

それらの文字は encodeURIComponent() 関数などでエンコードができます。


readyState プロパティ[編集]

readyState プロパティについては、次表のように仕様が決まっています。

readyState プロパティの意味
戻り値 意味
XMLHttpRequest.UNSENT(数値 0) オブジェクトが構築された。
XMLHttpRequest.OPENED(数値 1) open()メソッドが正常に呼び出されました。この状態では、setRequestHeader()を使ってリクエスト・ヘッダーを設定し、send()メソッドを使ってフェッチを開始することができます。
XMLHttpRequest.HEADERS_RECEIVED(数値 2) すべてのリダイレクト(もしあれば)が行われ、応答のすべてのヘッダーが受信されていること。
XMLHttpRequest.LOADING(数値 3) レスポンスボディの受信中です。
XMLHttpRequest.DONE(数値 4) データの転送が完了した、または転送中に何か問題が発生した(無限リダイレクトなど)。

HTTPステータスコードは、いくつもありますが、今回のコードに関係ありそうな主要なものは下記の通りです。下記の表以外にもいくつもありますが、要するに「200」であれば「成功」ですし、「200」以外なら通常は失敗です。

また、「XMLHttpRequest.DONE」でデータの転送が完了、あるいは転送中に何か問題が発生し(無限リダイレクトなど)異常終了です。


なお、普通の通信なら readyState プロパティは XMLHttpRequest.UNSENT → XMLHttpRequest.OPENED → XMLHttpRequest.HEADERS_RECEIVED → XMLHttpRequest.LOADING → XMLHttpRequest.DONE の順番で変化していきます(サンプルコードの表示結果もこのような順番になっています)。

status プロパティ[編集]

status プロパティの意味
戻り値 意味
200 成功
404 リクエストされたリソースが見つからない (Not Found)
URLなどが間違っているのが原因
500 サーバーのエラー。サーバーダウンの場合もこれに該当。

上述の readyState プロパティおよび status プロパティの説明を合わせて考えれば、readyState 「4」および status 「200」ならば、とりあえず通信の環境構築には成功しています。

send()メソッドなど[編集]

コード末尾のほうにある send() メソッドは、ここでは「通信を開始せよ」程度の意味である。「GET」コマンドでも「POST」コマンドでも、どちらの場合でも、send() メソッドによって通信を開始する。

send() メソッドによって

xhr.onreadystatechange = function(){      }

で指定した関数の内部が実行される。なお onreadystatechange プロパティは「オン・レディ・ステイト・チェンジ」である。


document.write("
" + xhr.responseText + "
");
にある responseText プロパティは、通信で帰って来たテキストを意味するプロパティである。

高度なコード[編集]

イベントリスナーによるイベントハンドリング
const req = new XMLHttpRequest();
req.addEventListener("progress", (ev) => {
  if (ev.lengthComputable) {
    const percent = ev.loaded / ev.total * 100;
    // 進捗表示
    console.log("${percent}%転送しました。")
  }
  else {
    // 長さ不明
    console.log("転送中。")
  }
});
req.addEventListener("loadstart", (ev) => console.log("転送を開始しました。"));
req.addEventListener("load", (ev) => console.log("転送しました。"));
req.addEventListener("loadend", (ev) => console.log("転送が終了しました。"));
req.addEventListener("error", (ev) => console.log("ファイルの転送中にエラーが発生しました。"));
req.addEventListener("abort", (ev) => console.log("ユーザーが転送をキャンセルしました。"));
req.addEventListener("timeout", (ev) => console.log("転送がタイムアウトしました。"));
req.addEventListener("readystatechange", (ev) => {
    switch (req.readyState) {
    case XMLHttpRequest.UNSENT:
        console.log("UNSENT");
        break;
    case XMLHttpRequest.OPENED:
        console.log("OPENED");
        break;
    case XMLHttpRequest.HEADERS_RECEIVED:
        console.log("HEADERS_RECEIVED");
        break;
    case XMLHttpRequest.LOADIND:
        console.log("LOADIND");
        break;
    case XMLHttpRequest.DONE:
        const status = req.status;
        if (status === 0 || (status >= 200 && status < 400)) {
            console.log(req.responseText);
        }
        else {
            console.log(req.responseXML);
            console.log(req.responseText);
        }
        break;
    default :
        console.log(`${req.readyState} は仕様にないXMLHttpRequest.readyState`);
    }
});
req.open('GET', './example.xml', false);
req.send(null);
※ イベントリスナーは open() を呼び出す前に追加する必要があります(もし、そうしないのであれば progress イベントは発火しません)。

資料[編集]

XMLHttpRequestの静的プロパティ[編集]

XMLHttpRequest.UNSENT
0 : number
XMLHttpRequest.OPENED
1 : number
XMLHttpRequest.HEADERS_RECEIVED
2 : number
XMLHttpRequest.LOADING
3 : number
XMLHttpRequest.DONE
4 : number
XMLHttpRequest.arguments
null : object
XMLHttpRequest.caller
null : object
XMLHttpRequest.length
0 : number
XMLHttpRequest.name
"XMLHttpRequest" : string

XMLHttpRequestのインスタンスプロパティ ​[編集]

XMLHttpRequest.prototype.DONE
XMLHttpRequest.prototype.HEADERS_RECEIVED
XMLHttpRequest.prototype.LOADING
XMLHttpRequest.prototype.OPENED
XMLHttpRequest.prototype.UNSENT
XMLHttpRequest.prototype.abort
XMLHttpRequest.prototype.constructor()
XMLHttpRequest.prototype.getAllResponseHeaders
XMLHttpRequest.prototype.getResponseHeader
XMLHttpRequest.prototype.onreadystatechange
XMLHttpRequest.prototype.open
XMLHttpRequest.prototype.overrideMimeType
XMLHttpRequest.prototype.readyState
XMLHttpRequest.prototype.response
XMLHttpRequest.prototype.responseText
XMLHttpRequest.prototype.responseType
XMLHttpRequest.prototype.responseURL
XMLHttpRequest.prototype.responseXML
XMLHttpRequest.prototype.send
XMLHttpRequest.prototype.setRequestHeader
XMLHttpRequest.prototype.status
XMLHttpRequest.prototype.statusText
XMLHttpRequest.prototype.timeout
XMLHttpRequest.prototype.upload
XMLHttpRequest.prototype.withCredentials

脚注[編集]

  1. ^ 2021年6月の時点でディフォルトでFTPスキームを(かつてのgopherのように)無効にするブラウザが増えつつある。https://blog.chromium.org/2020/09/chrome-86-improved-focus-highlighting.html
  2. ^ 山田祥寛『JavaScript本格入門』、2019年8月17日 初版 第6刷発行、P395
  3. ^ 山田、P398

外部リンク[編集]

このページ「JavaScript/XMLHttpRequest」は、まだ書きかけです。加筆・訂正など、協力いただける皆様の編集を心からお待ちしております。また、ご意見などがありましたら、お気軽にトークページへどうぞ。