web-dev-qa-db-ja.com

XMLHttpRequest非同期が機能せず、常にステータス0を返します

これが私がw3schoolsから一緒に石畳にしたサンプルXMLHttpRequestです

<html>
<head>
<script type="text/javascript">
function loadXMLDoc()
{
  var T="nothing";

  xmlhttp=new XMLHttpRequest();
  xmlhttp.overrideMimeType('text/plain');  // don't sc
  xmlhttp.onreadystatechange=function()
  {
    alert ("rdystate: " + xmlhttp.readyState);
    alert ("status: "   + xmlhttp.status);
    alert ("Text: "     + xmlhttp.statusText);
    if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
      T = xmlhttp.responseText;
    }
  }
xmlhttp.open("GET","SBL_PROBES.htm",true);
xmlhttp.send(null);
//T = xmlhttp.responseText;
alert(T);
}
</script>
</head>
<body>

<h2>Using the XMLHttpRequest object</h2>
<div id="myDiv"></div>
<button type="button" onclick="loadXMLDoc()">CHange Content</button>

</body>
</html>

XMLHttpRequestは常にゼロのステータスを返します。

Firefoxのエラーコンソールには何も表示されません。

行を変更してリクエストを同期リクエストに変更した場合

xmlhttp.open("GET","SBL_PROBES.htm",true);

xmlhttp.open("GET","SBL_PROBES.htm",false);

行のコメントを解除します

//T = xmlhttp.responseText;

要求されたファイルのテキストが返されます。

HTMとファイルは同じディレクトリにあります。これを試してみると、そこにもファイルSBL_PROBES.htmが必要になりますが、その内容は関係ありません。

Firefox3.6.22を使用しています。

これはクロスドメインの問題でしょうか?もしそうなら、なぜそれは同期要求として機能するのですか?

12
Mike D

Ifステートメント内で関数を使用できます。この関数は、readystateが4に変化したときに実行されます。

var handleResponse = function (status, response) {
   alert(response)
}
var handleStateChange = function () {
   switch (xmlhttp.readyState) {
      case 0 : // UNINITIALIZED
      case 1 : // LOADING
      case 2 : // LOADED
      case 3 : // INTERACTIVE
      break;
      case 4 : // COMPLETED
      handleResponse(xmlhttp.status, xmlhttp.responseText);
      break;
      default: alert("error");
   }
}
var xmlhttp=new XMLHttpRequest();
xmlhttp.onreadystatechange=handleStateChange;
xmlhttp.open("GET","SBL_PROBES.htm",true);
xmlhttp.send(null);

古いコードは非同期呼び出しを行い、alertステートメントだけで続行しました。この時点でTは空でした。

わかりました。この全体がどのように機能するかを少し説明します。

最初に、handleResponseとhandleStateChangeという名前の2つのコールバック関数を定義します。これらはリクエストの後半で呼び出します。

その後、XMLHttpRequestを表すオブジェクトを作成します

var xmlhttp=new XMLHttpRequest();

これにより、オブジェクトは次のようになります(簡略化)。

XMLHttpRequest { status=0, readyState=0, multipart=false, onreadystatechange=handleEvent()}

Open(...)関数呼び出しを使用して、リクエストのパラメータを設定します。

xmlhttp.open("GET","SBL_PROBES.htm",true);

つまり、非同期のGETリクエストを実行して、ページSBL_PROBES.htmをフェッチします。次に、リクエスト自体を起動するsend(...)関数が呼び出されます。

ご想像のとおり、onreadystatechangeのコールバック関数を登録しました。これは実際にはeventHandlerです。状態が変化するたびに、この関数が呼び出されます。 (フォームでonKeyUpイベントにコールバック関数を登録する場合と同じです。このコールバックは、キーが上がるたびにトリガーされます:))

問題に関係する唯一のケースは状態4です。そのため、handleRequestコールバック関数は状態4でのみ呼び出されます。この時点で、Requestには実際に結果があり、さらにステータスがあります。 (ステータスとは、Webサーバーがステータスコード200 = ok、404 = not foundなどを返したことを意味します)

これはajaxのものの背後にあるすべての魔法ではありませんが、舞台裏で実際に起こっていることを簡単に説明する必要があります。これをWebサーバーでテストすることが重要です。テストにfile://を使用しないでください。

より詳細な情報が必要な場合は、お知らせください。

16
evildead

ステータスゼロは2つの理由で発生します。

  1. ファイルプロトコルを実行しています。
  2. Ajaxリクエストがアクティブなときに、何かがページをポストバックしています。

ここで#2を見ていると思います。 SOボタンのクリックをキャンセルする必要があります。

<button type="button" onclick="loadXMLDoc(); return false;">CHange Content</button>

上記のコードでは、リクエストが非同期の場合、alert(T)は常にnothingと表示します。

9
epascarello

その理由は、リクエストが戻る前にasyncが戻るからです。同期要求は、要求が戻った後に戻ります。

ここでロジックを操作してみてください。

xmlhttp.onreadystatechange=function()
  {
    alert ("rdystate: " + xmlhttp.readyState);
    alert ("status: "   + xmlhttp.status);
    alert ("Text: "     + xmlhttp.statusText);
    if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
      T = xmlhttp.responseText;
      alert(T);
    }
  }
3
Daniel A. White

非同期XMLHttpRequestopenステートメントを使用すると結果が得られないという問題と戦いました。この質問はグーグルを使用しているときに私が最初に見つけたので、これが私がそれを解決した方法です:

フォーム内にあるボタンを使用する場合は、type = "submit"およびonclick = "return myFunction()"に設定されていることを確認してください。そして、myFunction()で、必ずfalseを返します。nottrue!関数からtrueを返すことにより、ページをリロードするとXMLオブジェクトが消えます。 falseを返すと、XMLリクエストは完了するのに必要な時間を取得し、onreadystatechange関数が実行されます。

出典: Flaskメーリングリスト

1
Adalars1

私は今、この一般的な問題に対して良い反応を受け取りました。応答は次のとおりです。

これは、Web用に開発するときに非常に一般的な問題です。それを回避する方法は2つあります。

  1. 1つ目は、クエリパラメータ( "?callback = foo")を追加するときにAPIがサポートするJSONPを使用することです。これにより、すぐに稼働できるようになり、開発には最適ですが、ユーザーがAPIキーにアクセスできるため、本番環境での使用には安全ではありません。
  2. 2つ目(これはForecastで使用するものであり、本番環境に最適な方法です)は、ユーザーに代わってForecastにリクエストを送信できるプロキシサーバーを独自のドメインにセットアップすることです。これにより、ブラウザーの同一生成元ポリシーが回避され、ユーザーがAPIキー(サーバー側に保存できます)にアクセスできなくなり、必要に応じてリクエストキャッシュを利用できるようになります。 (私たちのお気に入りのWebサーバーであるNGINXは、これをすぐにサポートし、構成が非常に簡単です。サンプル構成が必要な場合は、お知らせください!)
0
Stan Truffaut