wake-up-neo.com

So verwalten Sie eine Umleitungsanforderung nach einem Ajax-Aufruf von jQuery

Ich benutze $.post(), um ein Servlet mit Ajax aufzurufen, und dann das resultierende HTML-Fragment, um ein div-Element auf der aktuellen Seite des Benutzers zu ersetzen. Wenn die Sitzung jedoch abgelaufen ist, sendet der Server eine Umleitungsanweisung, um den Benutzer zur Anmeldeseite zu senden. In diesem Fall ersetzt jQuery das Element div durch den Inhalt der Anmeldeseite, wodurch die Augen des Benutzers dazu gezwungen werden, tatsächlich eine seltene Szene zu sehen. 

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

1249
Elliot Vargas

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

Zum Beispiel war unsere Wrapper-Funktion ungefähr so:

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

Dann haben wir beim Ajax-Aufruf Folgendes verwendet:

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

Dies funktionierte für uns, da bei allen Ajax-Aufrufen immer HTML-Werte in einem DIV-Element zurückgegeben wurden, mit dem ein Teil der Seite ersetzt wird. Außerdem mussten wir nur zur Anmeldeseite umleiten.

88
Elliot Vargas

Ich habe diese Frage gelesen und den Ansatz implementiert, der darin besteht, den Antwortstatuscode auf 278 zu setzen, um zu vermeiden, dass der Browser die Weiterleitungen transparent behandelt. Obwohl dies funktioniert hat, war ich etwas unzufrieden, da es ein bisschen hack ist.

Nachdem ich mich weiter umgesehen hatte, gab ich diesen Ansatz auf und benutzte JSON . In diesem Fall haben alle Antworten auf Ajax-Anforderungen 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 es tun muss.

Ich hatte ein ähnliches Problem wie Sie. Ich führe eine Ajax-Anfrage mit zwei möglichen Antworten aus: Eine, die den Browser auf eine neue Seite umleitet, und eine, die ein vorhandenes HTML-Formular auf der aktuellen Seite durch ein neues ersetzt. Der Jquery-Code dafür sieht ungefähr 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.

654
Steg

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. Eine JavaScript-Funktion an das Ereignis ajaxSuccess binden und prüfen, ob der Header vorhanden ist:

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

Kein Browser verarbeitet die Antworten von 301 und 302 korrekt. Und in der Tat sagt der Standard sogar, dass sie "transparent" damit umgehen sollten, was für die Hersteller von Ajax-Bibliotheken ein MASSIV-Kopfschmerz ist. In Ra-Ajax mussten wir den HTTP-Antwortstatuscode 278 (nur ein "nicht verwendeter" Erfolgscode) verwenden, um transparente Umleitungen vom Server zu verarbeiten ...

Das ärgert mich wirklich, und wenn hier jemand ein "Pull" in W3C hat, würde ich mich freuen, dass Sie W3C know lassen könnten, dass wir wirklich 301 und 302 Codes selbst handhaben müssen ...! ;)

109
Thomas Hansen

Ich mag die Methode von Timmerz mit einer leichten Zitrone. Wenn Sie contentType von text/html erhalten, wenn Sie JSON erwarten, werden Sie höchstwahrscheinlich umgeleitet. In meinem Fall lade ich die Seite einfach neu, und sie wird zur Anmeldeseite weitergeleitet. Oh, und überprüfen Sie, ob der Status von jqXHR 200 ist, was dumm erscheint, weil Sie in der Fehlerfunktion sind, oder? Andernfalls erzwingen legitime Fehlerfälle ein iteratives Nachladen (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();
    }

});
60
BrianY

Verwenden Sie den einfachen Aufruf $.ajax():

$.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';
}
46
Till

Ich wollte nur meinen Ansatz mitteilen, da dies möglicherweise jemandem helfen könnte:

Ich habe im Wesentlichen ein JavaScript-Modul hinzugefügt, das die Authentifizierung wie die Anzeige des Benutzernamens und auch die Behandlung der Weiterleitung auf die Anmeldeseite ausführt.

Mein Szenario: Wir haben grundsätzlich einen ISA Server, zwischen dem alle Anfragen abgerufen werden und mit einem 302 und einem Standortheader antwortet auf unserer Anmeldeseite.

In meinem JavaScript-Modul war mein Anfangsansatz so etwas

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

Das Problem (wie viele hier bereits erwähnt wurden) ist, dass der Browser die Weiterleitung selbst ausführt, weshalb mein ajaxComplete-Callback nie aufgerufen wurde. Stattdessen bekam ich die Antwort der bereits umgeleiteten Anmeldeseite , die offensichtlich ein status 200 war. Das Problem: Wie stellen Sie fest, ob die erfolgreiche 200-Antwort die eigentliche Anmeldeseite oder nur eine beliebige andere Seite ist?

Die Lösung

Da ich 302 Weiterleitungsantworten nicht erfassen konnte, habe ich auf meiner Anmeldeseite einen LoginPage-Header hinzugefügt, der die URL der Anmeldeseite selbst enthielt. Im Modul höre ich nun auf den Header und mache eine Umleitung:

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

... und das wirkt wie charmant :). Sie fragen sich vielleicht, warum ich die URL in den LoginPage-Header einfüge ... im Grunde, weil ich keine Möglichkeit gefunden habe, die URL von GET zu ermitteln, die sich aus der automatischen Standortumleitung aus dem xhr-Objekt ergibt ...

32
Juri

Ich weiß, dass dieses Thema alt ist, aber ich werde noch einen anderen Ansatz geben, den ich gefunden habe und zuvor hier beschrieben habe. Grundsätzlich verwende ich ASP.MVC mit WIF (dies ist jedoch nicht unbedingt wichtig für den Kontext dieses Themas - die Antwort ist unabhängig von den verwendeten Frameworks ausreichend. Der Hinweis bleibt unverändert - er behandelt Probleme, die während der Ausführung mit Authentifizierungsfehlern zusammenhängen Ajax-Anfragen)

Der unten gezeigte Ansatz kann auf alle ajax-Anforderungen angewendet werden (wenn sie das beforeSend-Ereignis offensichtlich nicht neu definieren).

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

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

[Authorize]
public virtual void CheckPulse() {}

Wenn der Benutzer nicht authentifiziert ist (das Token ist abgelaufen), kann auf diese Methode nicht zugegriffen werden (geschützt durch das Attribut Authorize). Da das Framework die Authentifizierung abwickelt und der Token abläuft, wird der Antwort der HTTP-Status 302 zugewiesen. Wenn Sie nicht möchten, dass Ihr Browser die Antwort 302 transparent behandelt, greifen Sie diese in Global.asax auf und ändern Sie den Antwortstatus - beispielsweise in 200 OK. Fügen Sie außerdem den Header hinzu, der Sie anweist, diese Antwort auf spezielle Weise (später auf der Clientseite) 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");
    }
}

Überprüfen Sie schließlich auf der Clientseite, ob ein solcher benutzerdefinierter Header vorhanden ist. Falls vorhanden, sollte die vollständige Weiterleitung zur Anmeldeseite erfolgen (in meinem Fall wird window.location durch die URL aus der Anforderung ersetzt, die automatisch von meinem Framework verarbeitet 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;
                }
            }
    });
}
28
jwaliszko

Ich denke, ein besserer Weg, um damit umzugehen, besteht darin, die vorhandenen HTTP-Protokoll-Antwortcodes zu nutzen, insbesondere 401 Unauthorized.

So habe ich es gelöst:

  1. Serverseite: Wenn die Sitzung abläuft und die Anforderung ajax lautet. Senden Sie einen 401-Antwortcode-Header
  2. Client-Seite: Binden Sie 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 Dies ist allgemeiner und Sie schreiben keine neuen benutzerdefinierten Angaben/Header. Sie sollten auch keine Ihrer vorhandenen ajax-Aufrufe ändern müssen.

Edit: Pro @ Robs Kommentar (401) (der HTTP-Statuscode für Authentifizierungsfehler) sollte der Indikator sein. Weitere Informationen finden Sie unter 403 Verbotene gegen 401 nicht autorisierte HTTP-Antworten . Einige Web-Frameworks verwenden 403 für Authentifizierungs- und Autorisierungsfehler. Passen Sie sie entsprechend an. Danke, Rob.

22
rynop

Ich habe dieses Problem so gelöst:

Fügen Sie eine Middleware zur Verarbeitung der Antwort hinzu. Wenn es sich um eine Weiterleitung für eine Ajax-Anforderung handelt, ändern Sie die Antwort in eine normale Antwort mit der Weiterleitungs-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

Wenn in ajaxComplete die Antwort eine Weiterleitung enthält, muss es sich um eine Weiterleitung handeln. Ändern Sie daher den Speicherort des Browsers.

$('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;
       }
   }
}
22
Tyr

Die meisten der angegebenen Lösungen verwenden eine Problemumgehung, die einen zusätzlichen Header oder einen unpassenden HTTP-Code verwendet. Diese Lösungen werden höchstwahrscheinlich funktionieren, fühlen sich aber etwas "hackig" an. Ich habe eine andere Lösung gefunden.

Wir verwenden WIF, das für die Umleitung einer passiven 401-Antwort konfiguriert ist (passiveRedirectEnabled = "true"). Die Umleitung ist bei der Bearbeitung normaler Anforderungen nützlich, funktioniert jedoch nicht für AJAX - Anforderungen (da Browser die 302/Umleitung nicht ausführen). 

Mit dem folgenden Code in Ihrer global.asax können Sie die Weiterleitung für AJAX - Anforderungen deaktivieren:

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

Auf diese Weise können Sie 401 Antworten für AJAX - Anforderungen zurückgeben, die Ihr Javascript dann durch erneutes Laden der Seite verarbeiten kann. Beim erneuten Laden der Seite wird eine 401 ausgegeben, die von WIF behandelt wird (und WIF leitet den Benutzer auf die Anmeldeseite um).

Ein Beispiel-Javascript, um 401 Fehler zu behandeln:

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

Eine andere Lösung (besonders nützlich, wenn Sie ein globales Verhalten festlegen möchten) ist die Verwendung der $.ajaxsetup()-Methode zusammen mit der statusCode-Eigenschaft . Verwenden Sie, wie bereits erwähnt, keinen Umleitungsstatuscode (3xx), sondern einen 4xx-Statuscode und handhaben Sie die Umleitungs-Clientseite.

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

Ersetzen Sie 400 durch den Statuscode, den Sie behandeln möchten. Wie bereits erwähnt, könnte 401 Unauthorized eine gute Idee sein. Ich benutze den 400, da er sehr unspezifisch ist, und ich kann den 401 für spezifischere Fälle verwenden (z. B. falsche Anmeldeinformationen). Anstatt direkt umzuleiten, sollte Ihr Backend einen 4xx-Fehlercode zurückgeben, wenn die Sitzung abgelaufen ist und Sie die Umleitung auf der Clientseite durchführen. Funktioniert perfekt für mich selbst mit Frameworks wie backbone.js

17
morten.c

Dieses Problem kann dann bei Verwendung der ASP.NET MVC-Methode RedirectToAction auftreten. Um zu verhindern, dass die Antwort in div angezeigt wird, können Sie einfach einen Ajax-Antwortfilter für eingehende Antworten mit $ .ajaxSetup ausführen. Wenn die Antwort eine MVC-Umleitung enthält, können Sie diesen Ausdruck auf JS-Seite auswerten. Beispielcode für JS unten:

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

Wenn die Daten wie folgt lauten: "window.location = '/ Acount/Login'" wird der Filter abgefangen und ausgewertet, um die Umleitung vorzunehmen, anstatt die Daten anzeigen zu lassen.

17

Zusammenstellen, was Vladimir Prudnikov und Thomas Hansen sagten:

  • Ändern Sie Ihren serverseitigen Code, um festzustellen, ob es sich um ein XHR handelt. Wenn ja, setzen Sie den Antwortcode der Umleitung auf 278 . In Django:
   if request.is_ajax():
      response.status_code = 278

Auf diese Weise behandelt der Browser die Antwort als Erfolg und übergibt sie Ihrem Javascript.

  • Stellen Sie in Ihrem JS sicher, dass das Formular über Ajax übermittelt wird, überprüfen Sie den Antwortcode und leiten Sie ihn ggf. um
$('#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); 
});
16
Graham King

Ich habe eine einfache Lösung, die für mich funktioniert, keine Server-Code-Änderung erforderlich ... fügen Sie einfach einen TL Muskatnuss hinzu ...

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

Ich überprüfe das Vorhandensein des HTML-Tags, aber Sie können den Index ändern, um nach der eindeutigen Zeichenfolge zu suchen, die auf Ihrer Anmeldeseite vorhanden ist ...

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

Versuchen

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

Lege es auf die Login-Seite. Wenn es auf der Hauptseite in einem Div geladen wurde, wird es auf die Anmeldeseite umgeleitet. "#site" ist eine ID eines div, das sich auf allen Seiten außer der Anmeldeseite befindet.

12
podeig

Die Antworten scheinen für Leute zu funktionieren, wenn Sie Spring Security verwenden. Ich habe jedoch die Erweiterung von LoginUrlAuthenticationEntryPoint und das Hinzufügen von spezifischem Code für die Verarbeitung von AJAX als robuster empfunden. Bei den meisten Beispielen werden all -Umleitungen nicht nur bei Authentifizierungsfehlern abgefangen. Dies war für das Projekt, an dem ich arbeite, nicht wünschenswert. Möglicherweise müssen Sie auch ExceptionTranslationFilter erweitern und die Methode "sendStartAuthentication" überschreiben, um den Zwischenspeicherungsschritt zu entfernen, wenn die fehlgeschlagene Anforderung AJAX nicht zwischengespeichert werden soll.

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

Quellen: 1 , 2

11
John

Ich habe dieses Problem gelöst, indem ich Folgendes in meine login.php-Seite eingefügt habe.

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

Einige könnten das Folgende nützlich finden:

Ich wollte, dass die Clients auf die Anmeldeseite umgeleitet werden, wenn eine Restaktion ohne Autorisierungstoken gesendet wird. Da alle meine Restaktionen auf Ajax basieren, brauchte ich eine gute generische Methode, um zur Anmeldeseite umzuleiten, anstatt mit der Ajax-Erfolgsfunktion umzugehen.

Das habe ich getan:

Auf einer Ajax-Anforderung sendet mein Server eine Json 200-Antwort "NEED TO AUTHENTICATE" (falls der Client sich authentifizieren muss).

Einfaches Beispiel in Java (serverseitig):

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

In meinem Javascript habe ich den folgenden Code hinzugefügt:

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

Und das ist es auch schon.

7
Tomer

Lassen Sie mich noch einmal das von @Steg beschriebene Problem anführen

Ich hatte ein ähnliches Problem wie Sie. Ich führe eine Ajax-Anfrage aus, die 2 .__ hat. Mögliche Antworten: Eine, die den Browser auf eine neue Seite umleitet, und eines, das ein vorhandenes HTML-Formular auf der aktuellen Seite durch ein neues .__ ersetzt. ein.

IMHO ist dies eine echte Herausforderung und muss offiziell auf die aktuellen HTTP-Standards erweitert werden.

Ich glaube, der neue HTTP-Standard besteht darin, einen neuen Statuscode zu verwenden. __ Bedeutung: Derzeit sagt 301/302 dem Browser, dass er den Inhalt von this request an eine neue location abrufen soll.

Im erweiterten Standard heißt es, wenn die Antwort status: 308 (nur ein Beispiel) ist, sollte der Browser die Hauptseite auf die angegebene location umleiten.

Das gesagt wird; Ich bin schon geneigt, dieses future - Verhalten nachzuahmen, und wenn also ein document.redirect benötigt wird, muss der Server antworten als:

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

Wenn JS den "status: 204" abruft, prüft es, ob der x-status: 308-Header vorhanden ist, und führt eine document.redirect-Seite zu der im location-Header angegebenen Seite durch.

Ist das für Sie sinnvoll?

7
Chaim Klar

in das Servlet sollten Sie response.setStatus(response.SC_MOVED_PERMANENTLY); eingeben, um den '301' xmlHttp-Status zu senden, den Sie für eine Umleitung benötigen ...

und in der $ .ajax-Funktion sollten Sie die .toString()-Funktion nicht verwenden, sondern nur

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

das Problem ist, dass es nicht sehr flexibel ist. Sie können sich nicht entscheiden, wohin Sie umleiten möchten.

das Umleiten durch die Servlets sollte der beste Weg sein. aber ich finde immer noch nicht den richtigen weg.

5
Juan Toro

Zum Schluss löse ich das Problem durch Hinzufügen eines benutzerdefinierten HTTP Header. Kurz vor der Antwort für jede Anfrage auf der Serverseite füge ich die aktuelle angeforderte URL zum Header der Antwort hinzu.

Mein Anwendungstyp auf dem Server ist Asp.Net MVC, und es ist ein guter Ort dafür. In Global.asax habe ich das Application_EndRequest-Ereignis so implementiert:

    public class MvcApplication : System.Web.HttpApplication
    {

    //  ...
    //  ...

        protected void Application_EndRequest(object sender, EventArgs e)
        {
            var app = (HttpApplication)sender;
            app.Context.Response.Headers.Add("CurrentUrl",app.Context. Request.CurrentExecutionFilePath);
        }

    }

Es funktioniert perfekt für mich! Nun habe ich in jeder Antwort der JQuery$.post die angeforderte url und auch andere Antwortheader, die als Ergebnis der POST-Methode durch den Status 302, 303, ... resultieren.

wichtig ist auch, dass der Code weder serverseitig noch clientseitig geändert werden muss.

und das nächste ist die Möglichkeit, auf die anderen Informationen der Nachaktion wie Fehler, Meldungen und ... auf diese Weise zuzugreifen.

Ich habe dies gepostet, vielleicht jemandem helfen :)

5
Ali Adlavaran

Mit der Header-Lösung hatte ich keinen Erfolg - sie wurden nie in meiner Methode ajaxSuccess/ajaxComplete erfasst. Ich habe Stegs Antwort mit der benutzerdefinierten Antwort verwendet, aber ich habe die JS-Seite geändert. Ich richte eine Methode ein, die ich in jeder Funktion aufrufe, so dass ich die Standardmethoden $.get und $.post verwenden kann.

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

Beispiel dafür im Einsatz ...

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;
}
5
jocull

Ich wollte mich einfach an alle Ajax-Anfragen für die gesamte Seite anschließen. @SuperG hat mich angefangen. Hier ist was ich endete mit:

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

Ich wollte gezielt nach bestimmten HTTP-Statuscodes suchen, um meine Entscheidung zu begründen. Sie können jedoch nur an ajaxError binden, um etwas anderes als den Erfolg zu erhalten (nur 200 vielleicht?)

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

Wenn Sie auch die Werte übergeben möchten, können Sie auch die Sitzungsvariablen festlegen und auf .__ zugreifen. Beispiel: In Ihrem jsp können Sie schreiben 

<% HttpSession ses = request.getSession(true);
   String temp=request.getAttribute("what_you_defined"); %>

Und dann können Sie diesen temporären Wert in Ihrer Javascript-Variablen speichern und herumspielen 

5
karthik339

Ich hatte dieses Problem mit einer Django-App, an der ich gerade bastele (Haftungsausschluss: Ich bastele daran zu lernen und bin in keiner Weise ein Experte). Ich wollte jQuery ajax verwenden, um eine DELETE-Anforderung an eine Ressource zu senden, sie auf der Serverseite zu löschen und dann eine Weiterleitung an (im Wesentlichen) die Startseite zu senden. Als ich HttpResponseRedirect('/the-redirect/') vom Python-Skript aus sendete, erhielt die ajax-Methode von jQuery 200 statt 302. Also habe ich eine Antwort von 300 mit:

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

Dann habe ich die Anfrage auf dem Client mit jQuery.ajax wie folgt gesendet/bearbeitet:

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

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

Vielleicht ist die Verwendung von 300 nicht "richtig", aber zumindest funktionierte es genau so, wie ich es wollte.

PS: Das war ein großer Aufwand für die mobile Version von SO. Der dumme ISP hat meine Serviceanspruchungsanforderung durchgesetzt, als ich mit meiner Antwort fertig war! 

4
Benny Jobigan

Sie können auch einen XMLHttpRequest-Sendeprototyp anhängen. Dies funktioniert für alle Sends (jQuery/dojo/etc) mit einem Handler.

Ich habe diesen Code geschrieben, um einen 500 abgelaufenen Fehler zu behandeln, aber es sollte genauso gut funktionieren, um eine 200-Weiterleitung abzufangen. Bereiten Sie den Wikipedia-Eintrag auf XMLHttpRequest onreadystatechange über die Bedeutung von readyState vor.

// 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);
}
4
Curtis Yallop

Außerdem möchten Sie möglicherweise den Benutzer zur angegebenen URL in den Kopfzeilen weiterleiten. So sieht es endlich aus:

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

UPD: Opps. Habe die gleiche Aufgabe, aber es funktioniert nicht. Mach das Zeug. Ich werde dir eine Lösung zeigen, wenn ich sie finde.

1

Ich habe eine funktionierende Lösung mit den Antworten von @John und @Arpad link und @RobWinch link

Ich verwende Spring Security 3.2.9 und jQuery 1.10.2.

Erweitern Sie die Klasse von Spring, um eine 4XX-Antwort nur aus AJAX -Anfragen zu erzeugen:

public class CustomLoginUrlAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {

    public CustomLoginUrlAuthenticationEntryPoint(final String loginFormUrl) {
        super(loginFormUrl);
    }

    // For AJAX requests for user that isn't logged in, need to return 403 status.
    // For normal requests, Spring does a (302) redirect to login.jsp which the browser handles normally.
    @Override
    public void commence(final HttpServletRequest request,
                         final HttpServletResponse response,
                         final AuthenticationException authException)
            throws IOException, ServletException {
        if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");
        } else {
            super.commence(request, response, authException);
        }
    }
}

applicationContext-security.xml

  <security:http auto-config="false" use-expressions="true" entry-point-ref="customAuthEntryPoint" >
    <security:form-login login-page='/login.jsp' default-target-url='/index.jsp'                             
                         authentication-failure-url="/login.jsp?error=true"
                         />    
    <security:access-denied-handler error-page="/errorPage.jsp"/> 
    <security:logout logout-success-url="/login.jsp?logout" />
...
    <bean id="customAuthEntryPoint" class="com.myapp.utils.CustomLoginUrlAuthenticationEntryPoint" scope="singleton">
        <constructor-arg value="/login.jsp" />
    </bean>
...
<bean id="requestCache" class="org.springframework.security.web.savedrequest.HttpSessionRequestCache">
    <property name="requestMatcher">
      <bean class="org.springframework.security.web.util.matcher.NegatedRequestMatcher">
        <constructor-arg>
          <bean class="org.springframework.security.web.util.matcher.MediaTypeRequestMatcher">
            <constructor-arg>
              <bean class="org.springframework.web.accept.HeaderContentNegotiationStrategy"/>
            </constructor-arg>
            <constructor-arg value="#{T(org.springframework.http.MediaType).APPLICATION_JSON}"/>
            <property name="useEquals" value="true"/>
          </bean>
        </constructor-arg>
      </bean>
    </property>
</bean>

Fügen Sie in meinen JSPs einen globalen AJAX Fehlerhandler hinzu, wie hier hier

  $( document ).ajaxError(function( event, jqxhr, settings, thrownError ) {
      if ( jqxhr.status === 403 ) {
          window.location = "login.jsp";
      } else {
          if(thrownError != null) {
              alert(thrownError);
          } else {
              alert("error");
          }
      }
  });

Entfernen Sie auch vorhandene Fehlerbehandlungsroutinen von AJAX Aufrufen in JSP-Seiten:

        var str = $("#viewForm").serialize();
        $.ajax({
            url: "get_mongoDB_doc_versions.do",
            type: "post",
            data: str,
            cache: false,
            async: false,
            dataType: "json",
            success: function(data) { ... },
//            error: function (jqXHR, textStatus, errorStr) {
//                 if(textStatus != null)
//                     alert(textStatus);
//                 else if(errorStr != null)
//                     alert(errorStr);
//                 else
//                     alert("error");
//            }
        });

Ich hoffe es hilft anderen.

Update1 Ich habe festgestellt, dass ich die Option (always-use-default-target = "true") zur form-login config ..__ hinzufügen musste. Dies war seit nach einem AJAX Anforderung wird auf die Anmeldeseite umgeleitet (aufgrund abgelaufener Sitzung), Spring merkt sich die vorherige AJAX -Anforderung und leitet sie nach dem Anmelden automatisch weiter. Dadurch wird der zurückgegebene JSON auf der Browserseite angezeigt. Natürlich nicht was ich will.

Update2 Verwenden Sie anstelle von always-use-default-target="true" das @RobWinch-Beispiel für das Blockieren von AJAX Anforderungen aus dem requstCache. Auf diese Weise können normale Links nach dem Anmelden zu ihrem ursprünglichen Ziel umgeleitet werden. AJAX Wechseln Sie nach dem Anmelden auf die Startseite.

1
Darren Parker