Frage So verwalten Sie eine Weiterleitungsanforderung nach einem jQuery Ajax-Aufruf


Ich benutze $.post() um ein Servlet mit Ajax aufzurufen und dann das resultierende HTML-Fragment zu verwenden, um a zu ersetzen div Element in der aktuellen Seite des Benutzers. Wenn die Sitzung jedoch abläuft, sendet der Server eine Umleitungsdirektive, um den Benutzer zur Anmeldeseite zu senden. In diesem Fall ersetzt jQuery die div Element mit dem Inhalt der Login-Seite, die Augen des Benutzers zwingen, eine seltene Szene in der Tat zu erleben.

Wie kann ich eine Redirect-Direktive von einem Ajax-Aufruf mit jQuery 1.2.6 verwalten?


1181
2017-10-13 21:29


Ursprung


Antworten:


Ich habe diese Frage gelesen und den Ansatz implementiert, der bezüglich des Festlegens des Antwortstatuscodes auf 278 ausgeführt wurde, um zu vermeiden, dass der Browser die Weiterleitungen transparent verarbeitet. Obwohl das funktionierte, war ich ein wenig unzufrieden, da es ein bisschen wie ein Hack ist.

Nachdem ich weiter gegraben habe, habe ich diesen Ansatz aufgegeben und benutzt JSON. In diesem Fall haben alle Antworten auf ajax-Anfragen den Statuscode 200 und der Hauptteil der Antwort enthält ein JSON-Objekt, das auf dem Server erstellt wird. Das Javascript auf dem Client kann dann das JSON-Objekt verwenden, um zu entscheiden, was zu tun ist.

Ich hatte ein ähnliches Problem wie deines. Ich führe eine Ajax-Anfrage durch, die zwei mögliche Antworten hat: Eine, die den Browser auf eine neue Seite umleitet und eine, die ein existierendes HTML-Formular auf der aktuellen Seite durch ein neues ersetzt. Der jquery-Code sieht so aus:

$.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);
        }
    }
});

Das JSON-Objekt "data" ist auf dem Server so aufgebaut, dass es zwei Member hat: data.redirect und data.form. Ich fand diesen Ansatz viel besser.


631
2017-10-07 22:54



Ich habe dieses Problem gelöst durch:

  1. Hinzufügen eines benutzerdefinierten Headers zur Antwort:

    public ActionResult Index(){
        if (!HttpContext.User.Identity.IsAuthenticated)
        {
            HttpContext.Response.AddHeader("REQUIRES_AUTH","1");
        }
        return View();
    }
    
  2. Binden einer JavaScript-Funktion an die ajaxSuccess Ereignis und prüfen, ob der Header existiert:

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

213
2017-11-20 08:39



Keine Browser verarbeiten 301 und 302 Antworten korrekt. Und tatsächlich sagt der Standard sogar, dass sie "transparent" mit ihnen umgehen sollten, was für die Ajax-Bibliotheksverkäufer ein MASSIVER Kopfschmerz ist. Im Ra-Ajax Wir mussten den HTTP-Response-Status-Code 278 (nur einen "unbenutzten" Erfolgscode) verwenden, um transparent vom Server weiterzuleiten ...

Das ärgert mich wirklich, und wenn hier jemand "Pull" in W3C hat, würde ich es begrüßen, dass Sie W3C haben könnten kennt dass wir wirklich 301 und 302 Codes selbst behandeln müssen ...! ;)


106
2018-01-27 18:10



Die Lösung, die schließlich implementiert wurde, bestand darin, einen Wrapper für die Callback-Funktion des Ajax-Aufrufs zu verwenden und in diesem Wrapper nach dem Vorhandensein eines bestimmten Elements im zurückgegebenen HTML-Chunk zu suchen. Wenn das Element gefunden wurde, führte der Wrapper eine Umleitung aus. Wenn nicht, leitete der Wrapper den Aufruf an die eigentliche Callback-Funktion weiter.

Zum Beispiel war unsere Wrapper-Funktion etwas wie:

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

Als wir den Ajax-Aufruf machten, verwendeten wir etwas wie:

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

Dies funktionierte für uns, weil alle Ajax-Aufrufe immer HTML innerhalb eines DIV-Elements zurückgegeben haben, das wir verwenden, um ein Stück der Seite zu ersetzen. Außerdem mussten wir nur auf die Anmeldeseite umleiten.


85
2017-08-23 19:21



Ich mag Timmerz 'Methode mit einer leichten Zitronenverdrehung. Wenn Sie jemals zurückkommen Inhaltstyp von Text / HTML wenn du erwartest JSON, Sie werden höchstwahrscheinlich umgeleitet. In meinem Fall lade ich einfach die Seite neu und es wird auf die Anmeldeseite umgeleitet. Oh, und überprüfen Sie, dass der jqXHR-Status 200 ist, was albern erscheint, weil Sie in der Fehlerfunktion sind, richtig? Andernfalls erzwingen legitime Fehlerfälle ein iteratives Neuladen (oops)

$.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();
    }

});

57
2017-10-13 21:54



Benutze das Low-Level $.ajax() Anruf:

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

Versuchen Sie dies für eine Weiterleitung:

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

44
2018-05-23 10:02



Ich wollte nur meinen Ansatz teilen, da dies vielleicht jemandem helfen könnte:

Ich habe im Grunde ein JavaScript-Modul, das die Authentifizierung Dinge wie die Anzeige des Benutzernamens und auch diesen Fall die Handhabung der Weiterleitung zur Anmeldeseite.

Mein Szenario: Wir haben grundsätzlich einen ISA-Server dazwischen, der alle Anfragen abfragt und hört antwortet mit einem 302 und einem Ortskopf auf unserer Login-Seite.

In meinem JavaScript-Modul mein erster Ansatz war so etwas wie

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

Das Problem (so viele hier bereits erwähnt) ist, dass der Browser die Weiterleitung selbst übernimmt, wofür meine ajaxComplete Rückruf wurde nie angerufen, aber stattdessen habe ich die Antwort der bereits umgeleiteten Anmeldeseite was offensichtlich war a status 200. Das Problem: Wie erkennen Sie, ob die erfolgreiche 200er-Antwort Ihre eigentliche Login-Seite oder nur eine beliebige andere Seite ist?

Die Lösung

Da ich 302 Weiterleitungsantworten nicht erfassen konnte, fügte ich ein LoginPage Header auf meiner Login-Seite, die die URL der Login-Seite selbst enthielt. Im Modul höre ich jetzt nach dem Header und mache eine Weiterleitung:

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

... und das funktioniert wie Charme :). Sie werden sich vielleicht wundern, warum ich die URL in die LoginPage header ... im Grunde genommen, weil ich keine Möglichkeit gefunden habe, die URL von GET resultierend aus der automatischen Ortsumleitung von der xhr Objekt...


31
2018-05-06 23:55



Ich weiß, dass dieses Thema alt ist, aber ich gebe noch einen weiteren Ansatz, den ich gefunden und beschrieben habe Hier. Im Grunde verwende ich ASP.MVC mit WIF (aber das ist nicht wirklich wichtig für den Kontext dieses Themas - die Antwort ist angemessen, egal welche Frameworks verwendet werden. Der Hinweis bleibt unverändert - behandelt Probleme im Zusammenhang mit Authentifizierungsfehlern während der Ausführung von Ajax-Anfragen).

Der unten gezeigte Ansatz kann auf alle Ajax-Anfragen out-of-the-box angewendet werden (wenn sie nicht vor Ereignis senden offensichtlich neu definieren).

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

Bevor eine Ajax-Anfrage durchgeführt wird CheckPulse Methode wird aufgerufen (die Controller-Methode, die am einfachsten sein kann):

[Authorize]
public virtual void CheckPulse() {}

Wenn der Benutzer nicht authentifiziert ist (Token ist abgelaufen), kann auf diese Methode nicht zugegriffen werden (geschützt durch Authorize Attribut). Da das Framework die Authentifizierung behandelt, während das Token abläuft, setzt es den HTTP-Status 302 auf die Antwort. Wenn Sie nicht möchten, dass Ihr Browser 302 response transparent behandelt, fangen Sie ihn in Global.asax ab und ändern Sie den Antwortstatus - zum Beispiel auf 200 OK. Außerdem fügen Sie Header hinzu, der Sie anweist, solche Antworten auf spezielle Weise (später auf der Client-Seite) zu verarbeiten:

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

Schließlich auf der Client-Seite nach solchen benutzerdefinierten Header suchen. Wenn vorhanden, sollte die vollständige Umleitung zur Anmeldeseite erfolgen (in meinem Fall) window.location wird durch URL aus Anfrage ersetzt, die automatisch von meinem Framework bearbeitet wird).

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;
                }
            }
    });
}

26
2017-10-23 16:27



Ich denke, ein besserer Weg, dies zu bewältigen, besteht darin, die vorhandenen Antwortcodes des HTTP-Protokolls gezielt zu nutzen 401 Unauthorized.

Hier ist, wie ich es gelöst habe:

  1. Server-Seite: Wenn die Sitzung abläuft und die Anfrage Ajax ist. sende einen 401-Antwort-Code-Header
  2. Client-Seite: Bindet an die Ajax-Ereignisse

    $('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, das ist generischer und Sie schreiben keine neue benutzerdefinierte Spezifikation / Kopfzeile. Sie sollten auch keine Ihrer bestehenden Ajax-Anrufe ändern müssen.

Bearbeiten: Per @ Robs Kommentar unten, 401 (der HTTP-Statuscode für Authentifizierungsfehler) sollte der Indikator sein. Sehen 403 Forbidden vs 401 Nicht autorisierte HTTP-Antworten für mehr Details. Einige Web-Frameworks verwenden 403 sowohl für Authentifizierungs- als auch für Autorisierungsfehler - passen Sie sich entsprechend an. Danke, Rob.


21
2018-05-01 21:42



Eine andere Lösung, die ich gefunden habe (besonders nützlich, wenn Sie ein globales Verhalten einstellen wollen), ist die Verwendung der $.ajaxsetup() Methode zusammen mit dem statusCode Eigentum. Wie andere hingewiesen haben, verwenden Sie keinen Redirect Statuscode (3xx), verwenden Sie stattdessen a 4xx Statuscode und handle die Redirect-Client-Seite.

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

Ersetzen 400 mit dem Statuscode, den Sie behandeln möchten. Wie schon erwähnt 401 Unauthorized könnte eine gute Idee sein. Ich verwende das 400 da es sehr unspezifisch ist und ich das verwenden kann 401 für spezifischere Fälle (wie falsche Login-Daten). Anstatt also direkt umzuleiten, sollte Ihr Backend ein a zurückgeben 4xx Fehlercode, wenn die Sitzung abgelaufen ist und Sie die Umleitung clientseitig abwickeln. Funktioniert perfekt für mich auch mit Frameworks wie backbone.js


17
2017-10-27 13:31