javascript - practices - jquery jsonpcallback




So verwalten Sie eine Weiterleitungsanforderung nach einem jQuery Ajax-Aufruf (20)

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.

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

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


Die meisten der angegebenen Lösungen verwenden eine Problemumgehung, indem sie einen zusätzlichen Header oder einen unangemessenen HTTP-Code verwenden. Diese Lösungen werden höchstwahrscheinlich funktionieren, fühlen sich aber ein wenig "hacky" an. Ich habe eine andere Lösung gefunden.

Wir verwenden WIF, das für eine Umleitung konfiguriert ist (passiveRedirectEnabled = "true") bei einer 401-Antwort. Die Umleitung ist nützlich, wenn normale Anfragen bearbeitet werden, funktioniert aber nicht für AJAX-Anfragen (da Browser die 302 / Umleitung nicht ausführen).

Mit dem folgenden Code in Ihrer global.asax können Sie die Weiterleitung für AJAX-Anfragen 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;
        }
    }

Dadurch können Sie 401 Antworten für AJAX-Anfragen zurückgeben, die Ihr JavaScript dann durch erneutes Laden der Seite verarbeiten kann. Beim Neuladen der Seite wird eine 401 ausgelöst, die von WIF verarbeitet wird (und WIF leitet den Benutzer zur 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);
    }
});

Eine andere Lösung, die ich gefunden habe (besonders nützlich, wenn Sie ein globales Verhalten festlegen wollen), ist die Verwendung der $.ajaxsetup() Methode zusammen mit der Eigenschaft statusCode . Wie bereits erwähnt, sollten Sie keinen Umleitungsstatuscode ( 3xx ) verwenden, stattdessen einen 4xx Statuscode verwenden und die Umleitung clientseitig handhaben.

$.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 die 400 da es sehr unspezifisch ist und ich kann die 401 für spezifischere Fälle (wie falsche Login-Daten) verwenden. Anstatt also direkt umzuleiten, sollte Ihr Backend einen 4xx Fehlercode zurückgeben, wenn die Sitzung abgelaufen ist und Sie die Client-Seite umleiten. Funktioniert perfekt für mich auch mit Frameworks wie backbone.js


Einige könnten das Folgende nützlich finden:

Ich wollte, dass Clients auf die Anmeldeseite für alle Ruheaktionen umgeleitet werden, die ohne Autorisierungstoken gesendet werden. Da alle meine Ruheaktionen auf Ajax basieren, brauchte ich eine gute allgemeine Möglichkeit, um auf die Anmeldeseite umzuleiten, anstatt die Ajax-Erfolgsfunktion zu bearbeiten.

Das habe ich getan:

Bei jeder Ajax-Anfrage gibt mein Server eine JSON-200-Antwort "BENÖTIGT ZU AUTHENTISIEREN" zurück (wenn 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.


Ich habe das 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>

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 mehr gegraben habe, habe ich diesen Ansatz aufgegeben und 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 vorhandenes 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.


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 das ajaxSuccess Ereignis und Überprüfen, ob der Header vorhanden ist:

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

Ich habe eine einfache Lösung, die für mich funktioniert, keine Änderung des Servercodes erforderlich ... füge einfach ein Teelöffel Muskat 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 von html-Tags, aber Sie können indexOf ändern, um nach dem einzigartigen String auf Ihrer Login-Seite zu suchen ...


Ich mag Timmerz 'Methode mit einer leichten Zitronenverdrehung. Wenn Sie jemals contentType von text / html erhalten, wenn Sie JSON erwarten, werden Sie 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();
    }

});

Ich weiß, dass dieses Thema alt ist, aber ich gebe noch einen anderen Ansatz, den ich hier gefunden und beschrieben here . Grundsätzlich verwende ich ASP.MVC mit WIF (aber das ist nicht wirklich wichtig für den Kontext dieses Themas - Antwort ist angemessen, egal welche Frameworks verwendet werden. Der Hinweis bleibt unverändert - Umgang mit Problemen im Zusammenhang mit Authentifizierungsfehlern beim Ausführen 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 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 (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. Falls vorhanden, sollte eine vollständige Umleitung zur Anmeldeseite erfolgen (in meinem Fall wird window.location durch die URL aus der 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;
                }
            }
    });
}

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 behandelt die Umleitung auf die Anmeldeseite behandelt .

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

In meinem JavaScript-Modul war mein erster Ansatz 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, weshalb mein ajaxComplete Callback nie aufgerufen wurde, sondern ich bekam die Antwort der bereits umgeleiteten Login Seite, die offensichtlich einen 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, habe ich auf meiner Anmeldeseite einen LoginPage Header hinzugefügt, der die URL der Anmeldeseite 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 fragen sich vielleicht, warum ich die URL in den LoginPage Header LoginPage ... im Grunde genommen, weil ich keine Möglichkeit gefunden habe, die URL von GET zu bestimmen, die aus der automatischen Standortumleitung vom xhr Objekt xhr ...


Im Servlet sollten Sie response.setStatus(response.SC_MOVED_PERMANENTLY); um den Status '301' xmlHttp zu senden, den Sie für eine Weiterleitung benötigen ...

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

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

Das Problem ist, dass es nicht sehr flexibel ist, man kann nicht entscheiden, wohin man umleiten möchte.

Das Umleiten durch die Servlets sollte der beste Weg sein. aber ich kann immer noch nicht den richtigen Weg finden, es zu tun.


Schließlich löse ich das Problem, indem ich einen benutzerdefinierten HTTP Header hinzufüge. Just before response for every request in server side, i add the current requested url to response's header.

My application type on server is Asp.Net MVC , and it has a good place to do it. in Global.asax i implemented the Application_EndRequest event so:

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

    }

It works perfect for me! Now in every response of the JQuery $.post i have the requested url and also other response headers which comes as result of POST method by status 302 , 303 ,... .

and other important thing is that there is no need to modify code on server side nor client side.

and the next is the ability to get access to the other information of post action such errors, messages, and ..., In this way.

I posted this, maybe help someone :)


Versuchen

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

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


Während die Antworten für Leute zu funktionieren scheinen, wenn Sie Spring Security verwenden, habe ich gefunden, LoginUrlAuthenticationEntryPoint zu erweitern und spezifischen Code hinzuzufügen, um AJAX robuster zu behandeln. Die meisten Beispiele fangen alle Weiterleitungen ab, nicht nur Authentifizierungsfehler. 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 AJAX-Anforderung 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


Was Vladimir Prudnikov und Thomas Hansen gesagt haben:

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

Dadurch behandelt der Browser die Antwort als erfolgreich und gibt sie an Ihr Javascript weiter.

  • Stellen Sie in Ihrem JS sicher, dass die Übermittlung des Formulars über Ajax erfolgt, überprüfen Sie den Antwortcode und leiten Sie bei Bedarf 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); 
});

das hat bei mir funktioniert:

success: function(data, textStatus, xhr) {

        console.log(xhr.status);
}

Bei Erfolg erhält Ajax den gleichen Statuscode, den der Browser vom Server erhält, und führt ihn aus.


Additionally you will probably want to redirect user to the given in headers URL. So finally it will looks like this:

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

UPD: Opps. Have the same task, but it not works. Doing this stuff. I'll show you solution when I'll find it.


I was having this problem on a django app I'm tinkering with (disclaimer: I'm tinkering to learn, and am in no way an expert). What I wanted to do was use jQuery ajax to send a DELETE request to a resource, delete it on the server side, then send a redirect back to (basically) the homepage. When I sent HttpResponseRedirect('/the-redirect/') from the python script, jQuery's ajax method was receiving 200 instead of 302. So, what I did was to send a response of 300 with:

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

Then I sent/handled the request on the client with jQuery.ajax like so:

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

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

Maybe using 300 isn't "right", but at least it worked just like I wanted it to.

PS :this was a huge pain to edit on the mobile version of SO. Stupid ISP put my service cancellation request through right when I was done with my answer!


You can also hook XMLHttpRequest send prototype. This will work for all sends (jQuery/dojo/etc) with one handler.

I wrote this code to handle a 500 page expired error, but it should work just as well to trap a 200 redirect. Ready the wikipedia entry on XMLHttpRequest onreadystatechange about the meaning of readyState.

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




redirect