[Javascript] Come gestire una richiesta di reindirizzamento dopo una chiamata Ajax jQuery


Answers

Ho risolto questo problema con:

  1. Aggiunta di un'intestazione personalizzata alla risposta:

    public ActionResult Index(){
        if (!HttpContext.User.Identity.IsAuthenticated)
        {
            HttpContext.Response.AddHeader("REQUIRES_AUTH","1");
        }
        return View();
    }
    
  2. ajaxSuccess una funzione JavaScript all'evento ajaxSuccess e verifica se l'intestazione esiste:

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

Sto usando $.post() per chiamare un servlet usando Ajax e quindi usando il frammento HTML risultante per sostituire un elemento div nella pagina corrente dell'utente. Tuttavia, se la sessione scade, il server invia una direttiva di reindirizzamento per inviare l'utente alla pagina di accesso. In questo caso, jQuery sostituisce l'elemento div con i contenuti della pagina di accesso, costringendo gli occhi dell'utente a testimoniare una scena rara.

Come posso gestire una direttiva di reindirizzamento da una chiamata Ajax con jQuery 1.2.6?




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.




Ho risolto questo mettendo il seguente nella mia pagina login.php.

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



Volevo solo aggrapparmi a qualsiasi richiesta di ajax per l'intera pagina. @SuperG mi ha fatto iniziare. Ecco cosa ho finito con:

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

Volevo verificare in particolare determinati codici di stato http per basare la mia decisione su. Tuttavia, puoi semplicemente associare a ajaxError per ottenere qualcosa di diverso dal successo (solo 200 forse?) Potrei aver appena scritto:

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



Mettendo insieme ciò che hanno detto Vladimir Prudnikov e Thomas Hansen:

  • Cambia il tuo codice lato server per rilevare se si tratta di un XHR. Se lo è, imposta il codice di risposta del reindirizzamento su 278. In django:
   if request.is_ajax():
      response.status_code = 278

Questo fa sì che il browser tratti la risposta come un successo e la consegna al tuo Javascript.

  • Nel tuo JS, assicurati che l'invio del modulo sia tramite Ajax, controlla il codice di risposta e reindirizza se necessario:
$('#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); 
});



Provare

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

Mettilo nella pagina di accesso. Se è stato caricato in un div sulla pagina principale, verrà reindirizzato alla pagina di accesso. "#site" è un ID di un div che si trova su tutte le pagine ad eccezione della pagina di accesso.




Questo problema può essere visualizzato utilizzando il metodo RedirectToAction di ASP.NET MVC. Per evitare che il modulo mostri la risposta in div, puoi semplicemente fare un qualche tipo di filtro di risposta ajax per le risposte in entrata con $ .ajaxSetup . Se la risposta contiene il reindirizzamento MVC, è possibile valutare questa espressione sul lato JS. Esempio di codice per JS di seguito:

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

Se i dati sono: "window.location = '/ Acount / Login'" sopra il filtro lo prenderà e valuterà di effettuare il reindirizzamento invece di lasciare che i dati vengano visualizzati.




So che questo argomento è vecchio, ma darò un altro approccio che ho trovato e precedentemente descritto here . Fondamentalmente sto usando ASP.MVC con WIF (ma questo non è molto importante per il contesto di questo argomento - la risposta è adeguata indipendentemente dal framework utilizzato. L'indizio rimane invariato, trattando i problemi relativi agli errori di autenticazione durante l'esecuzione delle richieste Ajax ) .

L'approccio mostrato di seguito può essere applicato a tutte le richieste ajax out of the box (se non ridefiniscono ovviamente l'evento beforeSend).

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

Prima di ogni richiesta CheckPulse viene eseguito il metodo CheckPulse (il metodo controller che può essere qualsiasi cosa più semplice):

[Authorize]
public virtual void CheckPulse() {}

Se l'utente non è autenticato (il token è scaduto) non è possibile accedere a tale metodo (protetto da attributo Authorize ). Poiché il framework gestisce l'autenticazione, mentre il token scade, inserisce lo stato http 302 nella risposta. Se non si desidera che il browser gestisca in modo trasparente la risposta 302, acquisirla in Global.asax e modificare lo stato della risposta, ad esempio 200 OK. Inoltre, aggiungi l'intestazione, che ti indica di elaborare tale risposta in modo speciale (più avanti sul lato client):

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

Infine, dal lato client, controlla l'intestazione personalizzata. Se presente, deve essere eseguito il reindirizzamento completo alla pagina di accesso (nel mio caso window.location viene sostituito window.location dalla richiesta che viene gestito automaticamente dal mio framework).

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



Ho risolto questo problema in questo modo:

Aggiungere un middleware per elaborare la risposta, se si tratta di un reindirizzamento per una richiesta Ajax, modificare la risposta a una risposta normale con l'url di reindirizzamento.

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

Quindi, in ajaxComplete, se la risposta contiene reindirizzamento, deve essere un reindirizzamento, quindi modificare la posizione del browser.

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

    }



Non ho avuto alcun successo con la soluzione di intestazione - non sono mai stati rilevati nel mio metodo ajaxSuccess / ajaxComplete. Ho usato la risposta di Steg con la risposta personalizzata, ma ho modificato il lato JS. Ho impostato un metodo che chiamo in ciascuna funzione, così posso usare i metodi standard $.get e $.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);
    }
}

Esempio in uso ...

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



questo ha funzionato per me:

success: function(data, textStatus, xhr) {

        console.log(xhr.status);
}

in caso di successo, ajax riceverà lo stesso codice di stato che il browser ottiene dal server ed eseguirà.




Alcuni potrebbero trovare il sotto utile:

Volevo che i client venissero reindirizzati alla pagina di login per ogni rest-action che viene inviata senza un token di autorizzazione. Poiché tutte le mie rest-action sono basate su Ajax, avevo bisogno di un buon modo generico per reindirizzare alla pagina di login invece di gestire la funzione di successo Ajax.

Questo è quello che ho fatto:

Su qualsiasi richiesta Ajax, il mio server restituirà una risposta JSON 200 "NECESSITÀ DI AUTENTICARE" (se il client deve autenticarsi).

Semplice esempio in Java (lato server):

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

Nel mio Javascript ho aggiunto il seguente codice:

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

E questo è tutto.




La soluzione che è stata infine implementata era quella di utilizzare un wrapper per la funzione di callback della chiamata Ajax e in questo wrapper verificare l'esistenza di un elemento specifico sul blocco HTML restituito. Se l'elemento è stato trovato, il wrapper eseguiva un reindirizzamento. In caso contrario, il wrapper ha inoltrato la chiamata alla funzione di callback effettiva.

Ad esempio, la nostra funzione wrapper era qualcosa del tipo:

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

Quindi, durante la chiamata Ajax, abbiamo usato qualcosa come:

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

Questo ha funzionato per noi perché tutte le chiamate Ajax hanno sempre restituito l'HTML all'interno di un elemento DIV che usiamo per sostituire un pezzo della pagina. Inoltre, abbiamo solo bisogno di reindirizzare alla pagina di accesso.




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!




Utilizza la chiamata $.ajax() basso livello:

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

Prova questo per un reindirizzamento:

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