Ajaxで通信中に他画面に遷移するとエラー処理が走ってしまうという現象に出くわしました。
これが、Win7のFirefoxのみで現象が発生して、GoogleChrome、IEでは発生しないというクセモノ……。(そもそも、要件にFirefox対応がないのに、突っ込まれまして、対応することになりました……。)
発生したコード
以下のように書いてたんですけど、これだとFirefoxは「fail」オプションのエラー処理が走ってしまう。
※システムがわかるようなもろもろのところは適宜削除・修正しています。
function loadInfoFunction() { $.ajax({ type : "POST", url : "path", dataType : "json", data : {id: 1} } }).done(function(data) { // 実行処理 }).fail(function(xmlHttpRequest, textStatus, errorThrown) { // エラー処理 }); }
ぐぬぬ……。
調べていくと、対処法が2種類でてきた。
Ajax中にページ更新する際の注意点
Ajaxで通信中にF5などを押してページをリロードすると通信エラーが帰ってきます。
jQueryのajaxのレスポンス待ち中にページ遷移するとerrorになるのを何とか回避する方法 – 目指せ非プログラマー
まず、題が何を言っているかというと あるリクエストを jQueryの$.
現在、関わっているシステムだと後者が有効でしたので、そちらを詳しく書いてきます。
対処法
上のブログだと、さらに外部リンクがあって、本来はここのお話しに対処法が書かれています。
handle ajax error when a user clicks refresh
i apologise if this is something i should be able to look up. all of the terms i wanted were way overloaded.. here is my problem: when i open a page, it fires off a whole series of ajax calls. if …
JQueryは、ユーザーがブラウザからURLを更新するか、リンクをクリックするか、URLを変更することによって、ページから離れた場所に移動するとエラーイベントをスローします。
だから、エラーが発生した場合に、判断する条件を加えているのですね。
上のajax通信を修正すると以下のようになります。
function loadInfoFunction() { $.ajax({ type : "POST", url : "path", dataType : "json", data : {id: 1} } }).done(function(data) { // 実行処理 }).fail(function(xmlHttpRequest, textStatus, errorThrown) { if(xmlHttpRequest.readyState == 0 || xmlHttpRequest.status == 0) return; // it's not really an error else // エラー処理 }); }
つまり、何を判断しているの?
上のソースを見る限り、エラーになった場合、判断条件が増えています。
自分の勉強のため、詳しくしらべてみました。
XMLHttpRequest は、クライアントとサーバーの間でデータを伝送するための機能をクライアント側で提供する APIです。
まず、XMLHttpRequest.readyStateプロパティは、リクエストの状態を unsigned short 型の値で返します。
値 | 状態 | 説明 |
---|---|---|
0 | UNSENT | open() がまだ呼び出されていない。 |
1 | OPENED | send() がまだ呼び出されていない。 |
2 | HEADERS_RECEIVED | send() が呼び出され、ヘッダーとステータスが通った。 |
3 | LOADING | ダウンロード中。responseText は断片的なデータを保持している。 |
4 | DONE | 一連の動作が完了した。 |
もうひとつのxmlHttpRequest.statusプロパティは、リクエストに対するレスポンスのステータスを unsigned short 型の値で返します。この値は HTTP リザルトコードです。たとえば、リクエストが成功した場合、200を返します。
HTTP リザルトコードが0になる場合を調べていくと、さまざまなパターンがあるようですね。しかも、サーバーからではなく、ブラウザが返すエラーだったり。通信が切断されていたり、時間以内に通信が帰ってこなかった場合に発生します。
上の記事を見る限り、FirefoxはHTTPリザルトコードが408(request timeout)の場合、0を返すそうで。
0: Timeout (通信タイムアウト)って認識で問題なさそう。
突き詰めていくと、以下のブログのように、エラーハンドリングするのがいいのかなって思います。
HTTPステータスコードを使ってAJAX通信を整理する – おやまのエンジニアリングブログ
クライアントサイドで AJAX通信をハンドリングするときに HTTPの ステータスコードを使うと可読性がアップする上にサーバーもクライアントも 整理できるので非常に便利であることが最近わかったので ここに使い方をここにまとめておきます。 実装例は以下のとおり。 私は以下のエラーに対してハンドリングを入れています。 401: Unauthroized (許可されていない) セッションが無かった場合に使う。セッションが切れていた場合は、440を使う。 403: Forbidden (禁断) ユーザーの権限ではアクセス出来ない場所にアクセスしようとした場合に返す。 404: Not Found (見つからない) ページが見つからなかった時にサーバーが勝手に返す。 409: Conflict (衝突) サーバー皮の状態とリクエストの状態が一致しない時に返す。 410: Gone (無い) リソースは無くなっていて、クライアント側でもリソースを破棄すべき時に返す。 440: Login Expired (ログイン期限切れ) セッションの有効期限が切れた時に使う。 0: Timeout (通信タイムアウト) これだけは特殊で、サーバーからではなく、ブラウザが返すエラー。 通信が切断されていたり、時間以内に通信が帰ってこなかった場合に発生する。 >=500: サーバーエラー系 いわゆるサーバーエラー系全て。 例外 205: Reset Content (コンテンツをリセット) ページのリソースが古い場合にリセットするように支持する時に返す。 ただし、 ステータスコード が200系なので、successの方に仕込む必要がある。
まとめ
クロスブラウザ対応はなかなか難しいですね。ブラウザの仕様によって対応が変わってきます。あと、Ajax通信にもっと詳しくなっていきたいですね。今回まとめた記事、ご参考になれば幸いです。
参考資料
XMLHttpRequest
XMLHttpRequest (XHR) オブジェクトを使用すると、サーバーと対話することができます。ページ全体を更新する必要なしに、データを受け取ることができます。これでユーザーの作業を中断させることなく、ウェブページの一部を更新することができます。 XMLHttpRequest は AJAX プログラミングで頻繁に使用されます。 XMLHttpRequest という名前ではあるものの、 XML だけでなくあらゆる種類のデータを受け取るために使用することができます。 通信においてサーバーからのイベントデータやメッセージデータの受信を含む必要があるのであれば、 Server-sent event の EventSource インターフェイスを使用することも検討してください。全二重の通信では、 WebSocket の方が良いかもしれません。 XMLHttpRequest を初期化するコンストラクターです。これは、他のメソッドを呼び出す前に呼び出さなければなりません。 このインターフェイスは、 XMLHttpRequestEventTarget および EventTarget のプロパティを継承します。 XMLHttpRequest.onreadystatechange EventHandler で、これは readyState 属性が変化する度に呼び出されます。 リクエストの状態を unsigned short で返します。 リクエストのエンティティ本文を含む ArrayBuffer , , , JavaScript オブジェクト, の何れかを、 XMLHttpRequest.responseType の値に応じて返します。 リクエストに対するレスポンスがテキスト形式で入った を返すか、リクエストが失敗した場合や、まだ送信されていない場合は null を返します。 XMLHttpRequest.responseType