javascript - レスポンス - xmlhttprequest リダイレクト させない




jQuery Ajax呼び出しの後にリダイレクト要求を管理する方法 (20)

$.post()を使用してAjaxを使用してサーブレットを呼び出し、結果のHTMLフラグメントを使用してユーザーの現在のページのdiv要素を置き換えます。 ただし、セッションがタイムアウトすると、サーバーはリダイレクト指示を送信してユーザーをログインページに送信します。 この場合、jQueryはdiv要素をログインページの内容に置き換えているため、ユーザの目にはまれなシーンがあります。

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


@Stegで説明されているように問題を再度引用しましょう

私はあなたと同様の問題を抱えていました。 2つの可能な回答を持つajaxリクエストを実行します.1つはブラウザを新しいページにリダイレクトし、もう1つは現在のページの既存のHTMLフォームを新しいページに置き換えます。

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

私は新しいHttp Standardが新しいステータスコードを使うことになると信じています。 意味:現在301/302は、ブラウザにこの要求の内容を新しいlocationフェッチするように指示しlocation

拡張標準では、レスポンスstatus: 308 (単なる例)であれば、ブラウザはメインページを提供されたlocationリダイレクトする必要があると言います。

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

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

JSは " status: 204 "を取得すると、 x-status: 308ヘッダーの有無をチェックし、 locationヘッダーに指定されたページにdocument.redirectを実行します。

これはあなたに何か意味がありますか?


Vladimir PrudnikovとThomas Hansenが言ったことをまとめる:

  • サーバー側のコードを変更してXHRかどうかを検出します。 そうであれば、リダイレクトの応答コードを278に設定します。djangoでは:
   if request.is_ajax():
      response.status_code = 278

これにより、ブラウザは応答を成功として扱い、それをあなたのJavascriptに渡します。

  • あなたのJSでは、フォーム提出がAjax経由であることを確認し、レスポンスコードをチェックし、必要に応じてリダイレクトします:
$('#my-form').submit(function(event){ 

  event.preventDefault();   
  var options = {
    url: $(this).attr('action'),
    type: 'POST',
    complete: function(response, textStatus) {    
      if (response.status == 278) { 
        window.location = response.getResponseHeader('Location')
      }
      else { ... your code here ... } 
    },
    data: $(this).serialize(),   
  };   
  $.ajax(options); 
});

この問題は、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'"の場合はフィルタを捕捉し、データを表示させる代わりにリダイレクトを行うように評価します。


サーブレットでは、 response.setStatus(response.SC_MOVED_PERMANENTLY); リダイレクトに必要な '301' xml HTTPステータスを送信する...

$ .ajax関数では.toString()関数を使うべきではありません。

if (xmlHttp.status == 301) { top.location.href = 'xxxx.jsp'; }

問題は非常に柔軟ではない、リダイレクトする場所を決めることができないということです。

サーブレットをリダイレクトするのが最善の方法です。 しかし、私はまだそれを行う正しい方法を見つけることができません。


一部の人は、以下のことが役に立つかもしれません:

私は、クライアントが認証トークンなしで送信された休憩のためにログインページにリダイレクトされることを望んでいました。 残りのすべてのアクションはAjaxベースなので、Ajaxの成功関数を処理するのではなく、ログインページにリダイレクトするための一般的な方法が必要でした。

これは私がやったことです:

どんなAjaxリクエストでも、私のサーバはJson 200の応答「クライアントが認証する必要があるならば、認証する必要があります」を返すでしょう。

Java(サーバー側)の簡単な例:

@Secured
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {

    private final Logger m_logger = LoggerFactory.getLogger(AuthenticationFilter.class);

    public static final String COOKIE_NAME = "token_cookie"; 

    @Override
    public void filter(ContainerRequestContext context) throws IOException {        
        // Check if it has a cookie.
        try {
            Map<String, Cookie> cookies = context.getCookies();

            if (!cookies.containsKey(COOKIE_NAME)) {
                m_logger.debug("No cookie set - redirect to login page");
                throw new AuthenticationException();
            }
        }
        catch (AuthenticationException e) {
            context.abortWith(Response.ok("\"NEED TO AUTHENTICATE\"").type("json/application").build());
        }
    }
}

私のJavascriptでは、次のコードを追加しました:

$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
    var originalSuccess = options.success;

    options.success = function(data) {
        if (data == "NEED TO AUTHENTICATE") {
            window.location.replace("/login.html");
        }
        else {
            originalSuccess(data);
        }
    };      
});

そしてそれはそれについてです。


低レベルの$.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';
}

最終的に実装されたソリューションは、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を返すからです。 また、ログインページにリダイレクトするだけで済みました。


特定のソリューションのほとんどは、余分なヘッダーまたは不適当なHTTPコードを使用して回避策を使用します。 これらのソリューションはおそらく動作しますが、ちょっと「ハッキリ」と感じます。 私は別の解決策を思いついた。

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

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回の応答を返すことができます.AJAXリクエストでは、ページをリロードすることでJavaScriptが処理できます。 ページをリロードすると401が投げられ、WIFによって処理されます(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. 応答にカスタムヘッダーを追加する:

    public ActionResult Index(){
        if (!HttpContext.User.Identity.IsAuthenticated)
        {
            HttpContext.Response.AddHeader("REQUIRES_AUTH","1");
        }
        return View();
    }
    
  2. JavaScript関数をajaxSuccessイベントにバインドし、ヘッダが存在するかどうかを確認する:

    $(document).ajaxSuccess(function(event, request, settings) {
        if (request.getResponseHeader('REQUIRES_AUTH') === '1') {
           window.location = '/';
        }
    });
    

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

応答を処理するミドルウェアを追加します.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;
       }
   }
}

私はこの質問を読んで、ブラウザが透過的にリダイレクトを処理するのを避けるために、応答ステータスコードを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"は、サーバー上にdata.redirectとdata.formの2つのメンバーを持つように構築されています。 私はこのアプローチがはるかに優れていることを発見しました。


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

私は基本的に、ユーザー名の表示やこの場合もログインページへのリダイレクトの処理のような認証処理を行うJavaScriptモジュールを含んでいました。

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

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

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

問題(既に述べた多くのもの)は、ブラウザがリダイレクトを自分で処理するため、私のajaxCompleteコールバックが一度も呼び出されなかったため、明らかにstatus 200だった既にリダイレクトされたログインページの応答を得ました。 問題:成功した200のレスポンスが実際のログインページか他の任意のページかどうかをどのように検出しますか?

ソリューション

私は302のリダイレクトレスポンスをキャプチャできなかったので、ログインページ自体のURLを含むLoginPageヘッダーをログインページに追加しました。 モジュールでは、私は今、ヘッダーをリッスンし、リダイレクトを行います:

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

...そしてそれは魅力のように働く:)。 なぜ私はLoginPageヘッダにURLを含めるのか不思議に思うかもしれません...基本的には、 xhrオブジェクトからの自動ロケーションリダイレクトの結果としてGETのURLを判断する方法が見つからなかったためです...


私はこれを処理するより良い方法は、既存の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ステータスコード)が指標になります。 詳細については、 403 Forbidden vs 401 Unauthorized HTTP responsesを参照してください。 その中で、いくつかのWebフレームワークでは、認証と認証の両方のエラーに403が使用されていると言われています。 おかげでロブ。


私はちょうどページ全体のための任意のajax要求にラッチしたいと思った。 @SuperGは私を始めました。 ここで私は結局何をしました:

// redirect ajax requests that are redirected, not found (404), or forbidden (403.)
$('body').bind('ajaxComplete', function(event,request,settings){
        switch(request.status) {
            case 301: case 404: case 403:                    
                window.location.replace("http://mysite.tld/login");
                break;
        }
});

私は特定のhttpステータスコードをチェックして、自分の決定に基づいていたかったのです。 しかし、あなたは単にajaxErrorにバインドして、成功以外のものを得ることができます(おそらく、おそらく200でしょうか?)私はちょうど書いたことができます:

$('body').bind('ajaxError', function(event,request,settings){
    window.location.replace("http://mysite.tld/login");
}

私は私のために動作する単純なソリューションを持って、サーバーコードの変更は必要ありません...ちょうどナツメグのTSPを追加...

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

私はhtmlタグの有無をチェックしますが、indexOfを変更して、ログインページに存在する一意の文字列を検索することができます。


試す

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

ログインページに入れてください。 メインページのdivにロードされている場合は、ログインページをリダイレクトします。 "#site"は、ログインページを除くすべてのページにあるdivのIDです。


さらに、ユーザーを指定されたヘッダーURLにリダイレクトすることもできます。最終的には次のようになります:

$.ajax({
    //.... other definition
    complete:function(xmlHttp){
        if(xmlHttp.status.toString()[0]=='3'){
        top.location.href = xmlHttp.getResponseHeader('Location');
    }
});

UPD:Opps。同じ作業をしても機能しません。このことをやっている。見つけたら解決策を見せてあげましょう。


プロトタイプを送信するXMLHttpRequestをフックすることもできます。これは、1つのハンドラを持つすべてのセンド(jQuery / dojo / etc)で機能します。

私は500ページの期限切れのエラーを処理するためにこのコードを書いたが、200のリダイレクトをトラップするだけでもうまくいくはずである。readyStateの意味について、XMLHttpRequest onreadystatechangeでwikipediaの項目を用意してください。

// Hook XMLHttpRequest
var oldXMLHttpRequestSend = XMLHttpRequest.prototype.send;

XMLHttpRequest.prototype.send = function() {
  //console.dir( this );

  this.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 500 && this.responseText.indexOf("Expired") != -1) {
      try {
        document.documentElement.innerHTML = this.responseText;
      } catch(error) {
        // IE makes document.documentElement read only
        document.body.innerHTML = this.responseText;
      }
    }
  };

  oldXMLHttpRequestSend.apply(this, arguments);
}

私はdjangoアプリでこの問題を抱えていました(免責事項:私は学びたいと思っていますが、専門家ではありません)。私がしたいのは、jQuery ajaxを使用してリソースにDELETE要求を送信し、サーバー側で削除してから、リダイレクトを(基本的に)ホームページに送り返すことでした。私HttpResponseRedirect('/the-redirect/')がpythonスクリプトから送信したとき、jQueryのajaxメソッドは302の代わりに200を受け取っていました。だから、私がしたのは300の応答を送ることでした:

response = HttpResponse(status='300')
response['Location'] = '/the-redirect/' 
return  response

その後、jQuery.ajaxを使ってクライアント上で要求を送信/処理しました。

<button onclick="*the-jquery*">Delete</button>

where *the-jquery* =
$.ajax({ 
  type: 'DELETE', 
  url: '/resource-url/', 
  complete: function(jqxhr){ 
    window.location = jqxhr.getResponseHeader('Location'); 
  } 
});

たぶん300を使用しているのは「正しい」わけではありませんが、少なくとも私はそれが欲しいのと同じように機能しました。

PS:これはSOのモバイル版で編集するのに大変な苦労でした。愚かなISPは私の答えで終わったとき私のサービスのキャンセル要求を右に置く!


私はヘッダー・ソリューションで何の成功もしていませんでした。彼らは私のajaxSuccess / ajaxCompleteメソッドでは決して拾われませんでした。私はカスタムレスポンスでStegの答えを使用しましたが、私はJS側をいくつか修正しました。私は標準$.get$.postメソッドを使うことができるように、各関数で呼び出すメソッドを設定しました。

function handleAjaxResponse(data, callback) {
    //Try to convert and parse object
    try {
        if (jQuery.type(data) === "string") {
            data = jQuery.parseJSON(data);
        }
        if (data.error) {
            if (data.error == 'login') {
                window.location.reload();
                return;
            }
            else if (data.error.length > 0) {
                alert(data.error);
                return;
            }
        }
    }
    catch(ex) { }

    if (callback) {
        callback(data);
    }
}

使用中の例...

function submitAjaxForm(form, url, action) {
    //Lock form
    form.find('.ajax-submit').hide();
    form.find('.loader').show();

    $.post(url, form.serialize(), function (d) {
        //Unlock form
        form.find('.ajax-submit').show();
        form.find('.loader').hide();

        handleAjaxResponse(d, function (data) {
            // ... more code for if auth passes ...
        });
    });
    return false;
}




redirect