jQuery Ajax呼び出し後のリダイレクトリクエストを管理する方法

私は$.post()を使ってAjaxを使ってサーブレットを呼び出し、その結果のHTMLフラグメントを使って現在のページのdiv要素を置き換えています。しかし、セッションがタイムアウトすると、サーバーはユーザーをログインページに送るためにリダイレクトディレクティブを送信します。この場合、jQueryはdiv要素をログインページのコンテンツに置き換えているため、ユーザーの目には実に珍しい光景が映ることになります。

jQuery 1.2.6でAjax呼び出しからのリダイレクトディレクティブを管理するにはどうすればよいですか?

質問へのコメント (5)

私はこの質問を読み、ブラウザがリダイレクトを透過的に処理しないように、応答 HTTPステータスコードを278に設定することに関して述べられたアプローチを実装しました。 これはうまくいきましたが、少しハックなので少し不満でした。

さらに掘り下げた後、このアプローチを捨て、JSONを使用しました。 この場合、AJAXリクエストへのすべての応答にはステータスコード 200があり、応答の本文にはサーバー上に構築されたJSONオブジェクトが含まれています。 クライアントのJavaScriptは、JSONオブジェクトを使用して、何をする必要があるかを決定できます。

私もあなたの問題と同じような問題がありました。 2つの応答が可能なAJAXリクエストを実行します。1つはブラウザを新しいページにリダイレクトし、もう1つは現在のページの既存のHTMLフォームを新しいページに置き換えします。 これを行うjQueryコードは次のようになります。

$.ajax({
    type: "POST",
    url: reqUrl,
    data: reqBody,
    dataType: "json",
    success: function(data, textStatus) {
        if (data.redirect) {
            // data.redirect contains the string URL to redirect to
            window.location.href = data.redirect;
        } else {
            // data.form contains the HTML for the replacement form
            $("#myform").replaceWith(data.form);
        }
    }
});

JSONオブジェクト「データ」はサーバー上に構築され、「data.redirect」と「data.form」の2つのメンバーがいます。 このアプローチははるかに優れていることがわかりました。

解説 (12)

私はこの問題を次のように解決しました。

1。 応答にカスタムヘッダーを追加する:

    public ActionResult Index(){。
        if(。!HttpContext.User.Identity.IsAuthenticated)。
        {。
            HttpContext.Response.AddHeader( "REQUIRES_AUTH"、 "1");。
        }。
        view();を返します。
    }。

2。 JavaScript関数をajaxSuccessイベントにバインドし、ヘッダーが存在するかどうかを確認します。

    $(ドキュメント).ajaxSuccess(function(event、request、settings){。
        if(request.getResponseHeader( 'REQUIRES_AUTH')=== '1'){。
           window.location = '/';。
        }。
    });。
解説 (10)

301と302の応答を正しく処理するブラウザはありません。 そして実際、この規格は、Ajax Libraryベンダーにとって大きな頭痛の種である「透過的に」それらを処理する必要があるとさえ述べています。 Ra-Ajaxでは、HTTP応答ステータスコード278(「未使用」の成功コード)を使用して、サーバーからの透過的なリダイレクトを処理する必要がありました。..

これは本当に私を困らせます、そしてここで誰かがW3Cでいくつかの「プル」を持っているなら、私たちが本当に301と302のコードを自分で処理する必要があることをW3Cに知らせしていただければ幸いです。..! ;)。

解説 (4)
ソリューション

最終的に実装されたソリューションは、Ajax呼び出しのコールバック関数にラッパーを使用し、このラッパーチェックで、返されたHTMLチャンクに特定の要素が存在することを確認することでした。 要素が見つかった場合、ラッパーはリダイレクトを実行しました。 そうでない場合、ラッパーは呼び出しを実際のコールバック関数に転送しました。

たとえば、ラッパー機能は次のようなものでした。

function cbWrapper(data, funct){
    if($("#myForm", data).length > 0)
        top.location.href="login.htm";//redirection
    else
        funct(data);
}

次に、Ajax呼び出しを行うときに、次のようなものを使用しました。

$.post("myAjaxHandler", 
       {
        param1: foo,
        param2: bar
       },
       function(data){
           cbWrapper(data, myActualCB);
       }, 
       "html"
);

これは、すべてのAjax呼び出しが常にページの一部を置き換えるために使用するDIV要素内にHTMLを返すため、うまくいきました。 また、ログインページにリダイレクトするだけで済みました。

解説 (3)

レモンを少しひねったティメルツの方法が好きです。 JSON を期待しているときに text / html contentType が返された場合、リダイレクトされる可能性が最も高いです。 私の場合、ページをリロードするだけで、ログインページにリダイレクトされます。 ああ、そしてjqXHRステータスが200であることを確認してください。これは、エラー関数を使用しているため、ばかげているようです? それ以外の場合、正当なエラーケースは反復リロード(おっと)を強制します。

$.ajax(
   error:  function (jqXHR, timeout, message) {
    var contentType = jqXHR.getResponseHeader("Content-Type");
    if (jqXHR.status === 200 && contentType.toLowerCase().indexOf("text/html") >= 0) {
        // assume that our login has expired - reload our current page
        window.location.reload();
    }

});
解説 (2)

低レベルの $.ajax() 呼び出しを使用する:

$.ajax({
  url: "/yourservlet",
  data: { },
  complete: function(xmlHttp) {
    // xmlHttp is a XMLHttpRquest object
    alert(xmlHttp.status);
  }
});

リダイレクトにはこれを試してください:

if (xmlHttp.code != 200) {
  top.location.href = '/some/other/page';
}
解説 (9)

これは誰かを助けるかもしれないので、私は自分のアプローチを共有したかっただけです。

基本的に、ユーザー名の表示などの認証を処理するJavaScriptモジュールと、この場合、ログインページへのリダイレクトを処理しました

私のシナリオ:基本的にISAサーバーがあり、その間にすべてのリクエストを聞き、 302と位置ヘッダーでログインページに応答します。

私のJavaScriptモジュールでは、私の初期アプローチはそのようなものでした。

$(document).ajaxComplete(function(e, xhr, settings){
    if(xhr.status === 302){
        //check for location header and redirect...
    }
});

問題は(ここですでに述べたように)ブラウザがリダイレクトを単独で処理するため、「ajaxComplete」コールバックが呼び出されることはありませんが、代わりに、すでにリダイレクトされたログインページの**応答を取得しました。ステータス200 `。 問題:成功した200応答が実際のログインページであるか、他の任意のページであるかをどのように検出しますか??

ソリューション。

302のリダイレクト応答をキャプチャできなかったため、ログインページ自体のURLを含む「LoginPage」ヘッダーをログインページに追加しました。 モジュールでヘッダーを聞いてリダイレクトを行います。

if(xhr.status === 200){
    var loginPageRedirectHeader = xhr.getResponseHeader("LoginPage");
    if(loginPageRedirectHeader && loginPageRedirectHeader !== ""){
        window.location.replace(loginPageRedirectHeader);
    }
}

。そしてそれは魅力のように機能します:)。. なぜ私が「LoginPage」ヘッダーにURLを含めるのか疑問に思うかもしれません。.well基本的には、「xhr」オブジェクトからの自動位置リダイレクトに起因する「GET」のURLを決定する方法が見つからなかったためです。.

解説 (3)

このトピックは古いことは知っていますが、私が見つけて以前に説明したここのもう1つのアプローチを示します。 基本的に私はWIFでASP.MVCを使用しています(ただし、これはこのトピックのコンテキストではそれほど重要ではありません-どのフレームワークを使用しても、答えは適切です。 手がかりは変更されません-ajaxリクエストの実行中の認証の失敗に関連する問題に対処します)

以下に示すアプローチは、ボックス外のすべてのajaxリクエストに適用できます(beforeSendイベントを明らかに再定義しない場合)。

$.ajaxSetup({
    beforeSend: checkPulse,
    error: function (XMLHttpRequest, textStatus, errorThrown) {
        document.open();
        document.write(XMLHttpRequest.responseText);
        document.close();
    }
});

ajaxリクエストを実行する前に、「CheckPulse」メソッドが呼び出されます(最も単純なものでもよいコントローラーメソッド)。

[Authorize]
public virtual void CheckPulse() {}

ユーザーが認証されていない場合(トークンが期限切れ)、そのようなメソッドにはアクセスできません(「Authorize」属性によって保護されています)。 フレームワークは認証を処理するため、トークンの有効期限が切れる一方で、httpステータス302が応答に表示されます。 ブラウザーで302応答を透過的に処理したくない場合は、Global.asaxでキャッチして、応答ステータスを変更します(たとえば、200 OKに変更します)。さらに、ヘッダーを追加します。ヘッダーは、そのような応答を特別な方法で処理するように指示します(後でクライアント側で):

protected void Application_EndRequest()
{
    if (Context.Response.StatusCode == 302
        && (new HttpContextWrapper(Context)).Request.IsAjaxRequest())
    {                
        Context.Response.StatusCode = 200;
        Context.Response.AddHeader("REQUIRES_AUTH", "1");
    }
}

最後に、クライアント側でそのようなカスタムヘッダーを確認します。 存在する場合-ログオンページへの完全なリダイレクトを行う必要があります(私の場合、「window.location」はリクエストからのURLに置き換えられ、私のフレームワークで自動的に処理されます)。

function checkPulse(XMLHttpRequest) {
    var location = window.location.href;
    $.ajax({
        url: "/Controller/CheckPulse",
        type: 'GET',
        async: false,
        beforeSend: null,
        success:
            function (result, textStatus, xhr) {
                if (xhr.getResponseHeader('REQUIRES_AUTH') === '1') {
                    XMLHttpRequest.abort(); // terminate further ajax execution
                    window.location = location;
                }
            }
    });
}
解説 (3)

私はこの問題を次のように解決しました。

プロセス応答にミドルウェアを追加します。ajaxリクエストのリダイレクトである場合は、リダイレクトURLを使用して応答を通常の応答に変更します。

class AjaxRedirect(object):
  def process_response(self, request, response):
    if request.is_ajax():
      if type(response) == HttpResponseRedirect:
        r = HttpResponse(json.dumps({'redirect': response['Location']}))
        return r
    return response

次に、ajaxCompleteで、応答にリダイレクトが含まれている場合、それはリダイレクトである必要があるため、ブラウザの場所を変更します。

$('body').ajaxComplete(function (e, xhr, settings) {
   if (xhr.status == 200) {
       var redirect = null;
       try {
           redirect = $.parseJSON(xhr.responseText).redirect;
           if (redirect) {
               window.location.href = redirect.replace(/\?.*$/, "?next=" + window.location.pathname);
           }
       } catch (e) {
           return;
       }
   }
}
解説 (0)

これを処理するより良い方法は、既存のHTTPプロトコル応答コード、具体的には「401 Unauthorized」を活用することだと思います。

これが私がそれを解決した方法です:

1。 サーバー側:セッションが期限切れになり、リクエストがajaxの場合。 401応答コードヘッダーを送信します。 2。 クライアント側:ajaxイベントのバインド。

    $( 'body').bind( 'ajaxSuccess'、function(event、request、settings){。
    if(401 == request.status){。
        window.location = '/ users / login';。
    }。
    })。bind( 'ajaxError'、function(event、request、settings){。
    if(401 == request.status){。
        window.location = '/ users / login';。
    }。
    });。

IMOこれはより一般的であり、新しいカスタムスペック/ヘッダーを作成していません。 また、既存のajax呼び出しを変更する必要はありません。

編集:以下の@Robのコメントごとに、401(認証エラーのHTTPステータスコード)がインジケーターになるはずです。 詳細については、https://stackoverflow.com/questions/3297048/403-forbidden-vs-401-unauthorized-http-responsesを参照してください。 そうは言っても、一部のWebフレームワークは認証エラーと承認エラーの両方に403を使用しています-それに応じて適応してください。 ロブに感謝します。

解説 (1)

与えられたソリューションのほとんどは、追加のヘッダーまたは不適切なHTTPコードを使用して、回避策を使用します。 これらのソリューションはおそらく機能しますが、少し「ハック」に感じます。 別の解決策を考え出しました。

401応答でリダイレクト(passiveRedirectEnabled = "true")するように構成されたWIFを使用しています。 リダイレクトは通常のリクエストを処理するときに便利ですが、AJAXリクエストでは機能しません(ブラウザーは302 / redirectを実行しないため)。

global.asaxで次のコードを使用すると、AJAXリクエストのリダイレクトを無効にできます。

    void WSFederationAuthenticationModule_AuthorizationFailed(object sender, AuthorizationFailedEventArgs e)
    {
        string requestedWithHeader = HttpContext.Current.Request.Headers["X-Requested-With"];

        if (!string.IsNullOrEmpty(requestedWithHeader) && requestedWithHeader.Equals("XMLHttpRequest", StringComparison.OrdinalIgnoreCase))
        {
            e.RedirectToIdentityProvider = false;
        }
    }

これにより、AJAXリクエストに対して401の応答を返すことができます。JavaScriptは、ページをリロードして処理できます。 ページを再読み込みすると、WIFが処理する401がスローされます(WIFはユーザーをログインページにリダイレクトします)。

401エラーを処理するためのJavaScriptの例:

$(document).ajaxError(function (event, jqxhr, settings, exception) {

    if (jqxhr.status == 401) { //Forbidden, go to login
        //Use a reload, WIF will redirect to Login
        location.reload(true);
    }
});
解説 (1)

この問題は、ASP.NET MVC RedirectToActionメソッドを使用して表示されることがあります。 フォームがdivで応答を表示しないようにするには、 $ .ajaxSetup を使用して応答をエンコードするためのある種のajax応答フィルターを実行するだけです。 応答にMVCリダイレクトが含まれている場合は、この式をJS側で評価できます。 以下のJSの例コード:

$.ajaxSetup({
    dataFilter: function (data, type) {
        if (data && typeof data == "string") {
            if (data.indexOf('window.location') > -1) {
                eval(data);
            }
        }
        return data;
    }
});

データが次の場合: "window.location = '/ Acount / Login' "上記のフィルターはそれをキャッチし、データを表示させるのではなく、リダイレクトを行うと評価します。

解説 (1)

私が見つけたもう1つの解決策(特にグローバルな動作を設定する場合に役立ちます)は、$ .ajaxsetup()メソッドstatusCodeプロパティと一緒に使用することです。 他の人が指摘したように、リダイレクトステータスコード( 3xx)を使用せず、代わりに 4xxステータスコードを使用して、リダイレクトクライアント側を処理します。

$.ajaxSetup({ 
  statusCode : {
    400 : function () {
      window.location = "/";
    }
  }
});

400を処理するステータスコードに置き換えます。 すでに述べたように、「401 Unauthorized」は良い考えかもしれません。 400は非常に不特定であるため、 400 `を使用します。 `401は、より具体的なケース(間違ったログイン資格情報など)に使用できます。 したがって、直接リダイレクトする代わりに、セッションがタイムアウトし、リダイレクトクライアント側を処理するときに、バックエンドが「4xx」エラーコードを返す必要があります。 backbone.jsのようなフレームワークを使用しても、私にとっては完璧に機能します。

解説 (2)

ウラジミール・プルドニコフとトーマス・ハンセンが言ったことをまとめると:

-サーバー側のコードを変更して、XHRであるかどうかを検出します。その場合は、リダイレクトの応答コードを278に設定します。 ジャンゴで:

request.is_ajax()の場合: response.status_code = 278。

これにより、ブラウザは応答を成功として扱い、JavaScriptに渡します。

-JSで、フォームの送信がAjax経由であることを確認し、応答コードを確認し、必要に応じてリダイレクトします。

$( '#my-form').submit(function(event){。 。 event.preventDefault();。 varオプション= {。 url:$(this).attr( 'action')、。 タイプ:「POST」、 完了:function(response、textStatus){。 if(response.status == 278){。 window.location = response.getResponseHeader( 'Location')。 }。 その他{。 ... ここにあなたのコード。 ... }。 }、。 データ:$(this).serialize()、。 };。 $ .ajax(オプション);。 });。

解説 (0)

私には効果的なシンプルなソリューションがあり、サーバーコードを変更する必要はありません。.ナツメグの小さじを追加するだけです。..

$(document).ready(function ()
{
    $(document).ajaxSend(
    function(event,request,settings)
    {
        var intercepted_success = settings.success;
        settings.success = function( a, b, c ) 
        {  
            if( request.responseText.indexOf( "" ) > -1 )
                window.location = window.location;
            else
                intercepted_success( a, b, c );
        };
    });
});

htmlタグの存在を確認しますが、indexOfを変更して、ログインページに存在する一意の文字列を検索できます。..

解説 (1)

やってみて。

    $(document).ready(function () {
        if ($("#site").length > 0) {
            window.location = "" + "Login/LogOn";
        }
    });

ログインページに配置します。 メインページのdivに読み込まれた場合は、ログインページまでリダイレクトされます。 「#site」は、ログインページを除くすべてのページにあるdivのIDです。

解説 (0)
    <script>
    function showValues() {
        var str = $("form").serialize();
        $.post('loginUser.html', 
        str,
        function(responseText, responseStatus, responseXML){
            if(responseStatus=="success"){
                window.location= "adminIndex.html";
            }
        });     
    }
</script>
解説 (0)

Spring Securityを使用している場合、答えは人に役立つようですが、LoginUrlAuthenticationEntryPointを拡張し、AJAXを処理するための特定のコードを追加する方がより堅 ⁇ であることがわかりました。 ほとんどの例では、認証の失敗だけでなく、すべてのリダイレクトを傍受します。 これは私が取り組んでいるプロジェクトにとって望ましくありませんでした。 失敗したAJAXリクエストをキャッシュしたくない場合は、ExceptionTranslationFilterを拡張し、「sendStartAuthentication」メソッドをオーバーライドしてキャッシュステップを削除する必要もあります。

例AjaxAwareAuthenticationEntryPoint:

public class AjaxAwareAuthenticationEntryPoint extends
    LoginUrlAuthenticationEntryPoint {

    public AjaxAwareAuthenticationEntryPoint(String loginUrl) {
        super(loginUrl);
    }

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        if (isAjax(request)) {
            response.sendError(HttpStatus.UNAUTHORIZED.value(), "Please re-authenticate yourself");
        } else {
        super.commence(request, response, authException);
        }
    }

    public static boolean isAjax(HttpServletRequest request) {
        return request != null && "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
    }
}

ソース: 1、[2][2]。

[2]:http://yoyar.com/blog/2012/06/dealing-with-the-spring-security-ajax-session-timeout-problem /。

解説 (4)

私はこれを私のlogin.phpページに配置することで解決しました。

<script type="text/javascript">
    if (top.location.href.indexOf('login.php') == -1) {
        top.location.href = '/login.php';
    }
</script>
解説 (0)

@Stegで説明されている問題をもう一度引用します。

私もあなたの問題と同じような問題がありました。 2のajaxリクエストを実行します。 可能な応答:ブラウザを新しいページにリダイレクトする応答。 現在のページにある既存のHTMLフォームを新しいものに置き換えるもの。 1。

私見これは本当の挑戦であり、現在のHTTP標準に正式に拡張する必要があります。

新しいHttp標準は新しいステータスコードを使用することになると思います。 意味:現在 301 / 302はブラウザーに this リクエストの内容を新しい locationにフェッチするように指示しています。

拡張標準では、応答が「status:308」(単なる例)の場合、ブラウザはメインページを提供された「場所」にリダイレクトする必要があると言います。

そうは言っても;私はすでにこの将来の動作を模倣する傾向があるため、document.redirectが必要な場合は、サーバーに次のように応答させます。

status: 204 No Content
x-status: 308 Document Redirect
x-location: /login.html

JSが「 status:204」を取得すると、 x-status:308ヘッダーの存在を確認し、document.redirectを locationヘッダーで提供されるページにリダイレクトします。

これはあなたにとって意味がありますか??

解説 (0)