1480 votes

Comment gérer une demande de redirection après un appel Ajax de jQuery ?

J'utilise $.post() pour appeler une servlet à l'aide d'Ajax, puis en utilisant le fragment HTML résultant pour remplacer un fichier de type div dans la page actuelle de l'utilisateur. Toutefois, si la session expire, le serveur envoie une directive de redirection pour renvoyer l'utilisateur vers la page de connexion. Dans ce cas, jQuery remplace l'élément div avec le contenu de la page de connexion, obligeant les yeux de l'utilisateur à assister à une scène rare.

Comment gérer une directive de redirection à partir d'un appel Ajax avec jQuery 1.2.6 ?

1 votes

(pas une réponse en tant que telle) - J'ai fait cela dans le passé en modifiant la bibliothèque jquery et en ajoutant une vérification de la page de connexion à chaque XHR complet. Ce n'est pas la meilleure solution car il faudrait le faire à chaque mise à jour, mais cela résout le problème.

1 votes

Voir la question connexe : stackoverflow.com/questions/5941933/

0 votes

En HttpContext.Response.AddHeader et vérifier le succès d'ajaxsetup est la voie à suivre.

58voto

Till Points 14673

Utilisez l'option de bas niveau $.ajax() appeler :

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

Essayez ceci pour une redirection :

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

35voto

Juri Points 14330

Je voulais juste partager mon approche car cela pourrait aider quelqu'un :

J'ai inclus un module JavaScript qui gère l'authentification, comme l'affichage du nom d'utilisateur et, dans ce cas, le traitement de l'adresse IP. rediriger vers la page de connexion .

Mon scénario : Nous avons essentiellement un serveur ISA entre les deux qui écoute toutes les demandes et répond avec un 302 et un en-tête d'emplacement à notre page de connexion.

Dans mon module JavaScript, mon approche initiale était quelque chose comme

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

Le problème (comme beaucoup l'ont déjà mentionné ici) est que le navigateur gère lui-même la redirection. ajaxComplete n'a jamais été appelé, mais à la place, j'ai reçu le message suivant réponse de la page de connexion déjà redirigée qui, de toute évidence, était un status 200 . Le problème : comment détecter si la réponse 200 réussie est votre page de connexion réelle ou une autre page arbitraire ?

La solution

Comme je n'étais pas en mesure de capturer les réponses de redirection 302, j'ai ajouté un fichier LoginPage sur ma page de connexion qui contenait l'url de la page de connexion elle-même. Dans le module, j'écoute maintenant l'en-tête et je fais une redirection :

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

...et cela fonctionne comme un charme :). Vous vous demandez peut-être pourquoi j'inclus l'url dans le champ LoginPage parce que je n'ai pas trouvé de moyen de déterminer l'url de l'en-tête de l'application GET résultant de la redirection automatique de l'emplacement à partir du xhr objet...

30voto

Jaroslaw Waliszko Points 6618

Je sais que ce sujet est ancien, mais je vais vous présenter une autre approche que j'ai trouvée et décrite précédemment. aquí . En fait, j'utilise ASP.MVC avec WIF. (mais ce n'est pas vraiment important dans le contexte de ce sujet - la réponse est adéquate quel que soit le cadre utilisé. L'indice reste inchangé - traiter les problèmes liés aux échecs d'authentification lors de l'exécution de requêtes ajax) .

L'approche présentée ci-dessous peut être appliquée d'emblée à toutes les requêtes ajax (si elles ne redéfinissent pas l'événement beforeSend évidemment).

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

Avant toute requête ajax CheckPulse est invoquée (la méthode du contrôleur qui peut être la plus simple) :

[Authorize]
public virtual void CheckPulse() {}

Si l'utilisateur n'est pas authentifié (le jeton a expiré), il ne peut pas accéder à cette méthode (protégée par Authorize attribut). Comme le framework gère l'authentification, lorsque le jeton expire, il met le statut http 302 à la réponse. Si vous ne voulez pas que votre navigateur traite la réponse 302 de manière transparente, attrapez-la dans Global.asax et changez le statut de la réponse - par exemple en 200 OK. De plus, ajoutez un en-tête, qui vous indique de traiter cette réponse d'une manière spéciale (plus tard, côté 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");
    }
}

Enfin, du côté client, on vérifie la présence d'un tel en-tête personnalisé. S'il est présent, une redirection complète vers la page de connexion doit être effectuée (dans mon cas window.location est remplacée par l'url de la requête qui est gérée automatiquement par mon 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;
                }
            }
    });
}

29voto

rynop Points 7818

Je pense qu'une meilleure façon de gérer cela est de tirer parti des codes de réponse existants du protocole HTTP, notamment 401 Unauthorized .

Voici comment je l'ai résolu :

  1. Côté serveur : Si la session expire, et que la demande est ajax, envoyez un en-tête de code de réponse 401.
  2. Côté client : Lier les événements 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';
    }
    });

Cette solution est plus générique et vous n'avez pas à écrire une nouvelle spécification/un nouvel en-tête personnalisé. Vous ne devriez pas non plus avoir à modifier vos appels ajax existants.

Edit : Conformément au commentaire de @Rob ci-dessous, 401 (le code d'état HTTP pour les erreurs d'authentification) devrait être l'indicateur. Voir Réponses HTTP 403 interdites et 401 non autorisées pour plus de détails. Cela dit, certains frameworks web utilisent 403 pour les erreurs d'authentification ET d'autorisation - il faut donc s'adapter en conséquence. Merci Rob.

25voto

Tyr Points 239

J'ai résolu ce problème de la manière suivante :

Ajouter un middleware pour traiter la réponse, si c'est une redirection pour une requête ajax, changer la réponse en une réponse normale avec l'url de redirection.

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

Puis dans ajaxComplete, si la réponse contient une redirection, il doit s'agir d'une redirection, donc changer l'emplacement du navigateur.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X