XMLHttpRequestオブジェクトでRedirectをハンドリングするには?(リダイレクトを拾う方法)

XMLHttpRequestオブジェクトから送信したリクエストに対し、サーバがリダイレクト応答(ステータスコード=302)を返したときのハンドリングってどうやればいいんだろうか?と悩んだのでその記録。

解決したかったのは、以下のような問題。

  1. XMLHttpRequestで送ったリクエストが、サーバで認証エラー(セッションタイムアウトなど)となる
  2. このときサーバは認証エラー(=401)を返すのではなく、ログインページへのリダイレクトを返す
  3. クライアント(XMLHttpRequest)は、リダイレクト応答をうけとり、自動的にリダイレクト先を追跡する
  4. 結果、クライアントはログインページのHTMLを取得する(このときステータスコード=200)
  5. しかし、そもそもクライアントはJSON形式のデータを期待しているためスクリプトエラーとなる

調べてみたところ、XMLHttpRequestはリダイレクト応答をうけとると何事もなかったかのようにリダイレクト先にリクエストを送信してしまうようだ。そのため、クライアント側ではリダイレクトされたかどうかが判別できない(ステータスコードは常に200)。これでは困るのでステータスコード以外について調べてみたところ、レスポンスヘッダーの取得はできるようだ。これは都合がいい。
というのは、今回問題が発生したWebアプリではXMLHttpRequestのレスポンスは『必ず』JSON形式(=text/plain)としている。なので、text/htmlのレスポンスが返されたら、それはリダイレクトとみなすことができる。
完全な解決策とはいえないけど、以下のコードで問題解決。

httpRequest.onreadystatechange = function() {
  if (httpRequest.readyState == 4 && httpRequest.status == 200) {
    var cType = httpRequest.getResponseHeader("Content-Type");
    if (cType.toLowerCase().indexOf("text/plain") == -1) 
      window.location.href = url;                                             ← ここで認証エラーとなったリクエストを再送→リダイレクト
    // 以下、省略