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


j'utilise $.post() appeler une servlet en utilisant Ajax, puis en utilisant le fragment HTML résultant pour remplacer un div élément dans la page actuelle de l'utilisateur. Cependant, si la session expire, le serveur envoie une directive de redirection pour envoyer l'utilisateur à la page de connexion. Dans ce cas, jQuery remplace le div élément avec le contenu de la page de connexion, forçant les yeux de l'utilisateur à assister à une scène rare en effet.

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


1181
2017-10-13 21:29


origine


Réponses:


J'ai lu cette question et mis en œuvre l'approche qui a été énoncée concernant la définition du code de statut de réponse à 278 afin d'éviter que le navigateur gère les redirections de manière transparente. Même si cela a fonctionné, j'étais un peu insatisfait car c'est un peu un hack.

Après plus de creuser autour, j'ai abandonné cette approche et utilisé JSON. Dans ce cas, toutes les réponses aux requêtes ajax ont le code d'état 200 et le corps de la réponse contient un objet JSON qui est construit sur le serveur. Le javascript sur le client peut ensuite utiliser l'objet JSON pour décider de ce qu'il doit faire.

J'ai eu un problème similaire au vôtre. J'effectue une requête ajax qui a 2 réponses possibles: une qui redirige le navigateur vers une nouvelle page et une qui remplace un formulaire HTML existant sur la page actuelle avec une nouvelle. Le code de jquery pour faire ceci ressemble à ceci:

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

L'objet JSON "data" est construit sur le serveur pour avoir 2 membres: data.redirect et data.form. J'ai trouvé cette approche beaucoup mieux.


631
2017-10-07 22:54



J'ai résolu ce problème par:

  1. Ajouter un en-tête personnalisé à la réponse:

    public ActionResult Index(){
        if (!HttpContext.User.Identity.IsAuthenticated)
        {
            HttpContext.Response.AddHeader("REQUIRES_AUTH","1");
        }
        return View();
    }
    
  2. Liaison d'une fonction JavaScript à ajaxSuccess événement et vérifiant pour voir si l'en-tête existe:

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

213
2017-11-20 08:39



Aucun navigateur ne gère correctement les réponses 301 et 302. Et en fait, la norme dit même qu'ils devraient les traiter «de manière transparente», ce qui est un casse-tête MASSIVE pour les fournisseurs de la bibliothèque Ajax. Dans Ra-Ajax nous avons été contraints d'utiliser le code d'état de réponse HTTP 278 (juste un code de succès "inutilisé") pour gérer de manière transparente les redirections du serveur ...

Cela m'énerve vraiment, et si quelqu'un ici a un peu "pull" dans le W3C j'apprécierais que vous puissiez laisser W3C connaître que nous avons vraiment besoin de manipuler les 301 et 302 codes nous-mêmes ...! ;)


106
2018-01-27 18:10



La solution qui a finalement été implémentée consistait à utiliser un wrapper pour la fonction de rappel de l'appel Ajax et, dans cette enveloppe, à vérifier l'existence d'un élément spécifique sur le bloc HTML renvoyé. Si l'élément a été trouvé, l'encapsuleur a exécuté une redirection. Si ce n'est pas le cas, l'encapsuleur a transféré l'appel à la fonction de rappel réelle.

Par exemple, notre fonction wrapper était quelque chose comme:

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

Ensuite, lors de l'appel Ajax, nous avons utilisé quelque chose comme:

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

Cela a fonctionné pour nous car tous les appels Ajax renvoyaient toujours du code HTML dans un élément DIV que nous utilisons pour remplacer une partie de la page. De plus, nous n'avions besoin que de rediriger vers la page de connexion.


85
2017-08-23 19:21



J'aime la méthode de Timmerz avec une légère torsion de citron. Si jamais vous êtes renvoyé contentType de text / html quand vous vous attendez JSON, vous êtes probablement redirigé. Dans mon cas, je recharge simplement la page, et elle est redirigée vers la page de connexion. Oh, et vérifiez que le statut jqXHR est 200, ce qui semble idiot, parce que vous êtes dans la fonction d'erreur, non? Sinon, des cas d'erreurs légitimes forceront un rechargement itératif (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



Utilisez le bas niveau $.ajax() appel:

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

44
2018-05-23 10:02



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

J'ai essentiellement inclus un module JavaScript qui gère les choses d'authentification comme l'affichage du nom d'utilisateur et aussi ce cas de manipulation de la rediriger vers la page de connexion.

Mon scénario: Nous avons essentiellement un serveur ISA entre lequel écoute toutes les demandes et répond avec un 302 et un en-tête de localisation à 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 mentionné ici) est que le navigateur gère lui-même la redirection ajaxComplete rappel n'a jamais été appelé, mais à la place j'ai reçu le réponse de la page de connexion déjà redirigée qui était évidemment un status 200. Le problème: comment pouvez-vous détecter si la réponse 200 réussie est votre page de connexion réelle ou juste une autre page arbitraire?

La solution

Comme je n'étais pas capable de capturer 302 réponses de redirection, j'ai ajouté LoginPage en-tête 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 le charme :). Vous pourriez vous demander pourquoi j'inclus l'URL dans le LoginPage en-tête ... bien fondamentalement parce que je n'ai trouvé aucun moyen de déterminer l'URL de GET résultant de la redirection automatique de l'emplacement de la xhr objet...


31
2018-05-06 23:55



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

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

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

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

[Authorize]
public virtual void CheckPulse() {}

Si l'utilisateur n'est pas authentifié (le jeton a expiré), cette méthode n'est pas accessible (protégée par Authorize attribut). Du fait que le cadre gère l'authentification, alors que 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-le dans Global.asax et changez l'état de la réponse - par exemple à 200 OK. En outre, ajoutez l'en-tête, ce qui vous demande de traiter cette réponse de façon particulière (plus tard du 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, côté client, recherchez un tel en-tête personnalisé. Si présent - la redirection complète vers la page de connexion doit être faite (dans mon cas window.location est remplacé 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;
                }
            }
    });
}

26
2017-10-23 16:27



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

Voici comment je l'ai résolu:

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

IMO c'est plus générique et vous n'écrivez pas de nouvelles spécifications / en-têtes personnalisées. Vous ne devriez pas avoir à modifier l'un de vos appels ajax existants.

Modifier: Le commentaire de Per @ Rob ci-dessous, 401 (le code d'état HTTP pour les erreurs d'authentification) devrait être l'indicateur. Voir 403 Interdit contre 401 Réponses HTTP non autorisées pour plus de détails. Cela étant dit, certains frameworks web utilisent 403 pour les erreurs d'authentification et d'autorisation - donc adaptez-les en conséquence. Merci Rob.


21
2018-05-01 21:42



Une autre solution que j'ai trouvée (particulièrement utile si vous voulez définir un comportement global) est d'utiliser $.ajaxsetup() méthode avec la statusCode propriété. Comme d'autres l'ont souligné, n'utilisez pas de code d'état de redirection (3xx), utilisez plutôt 4xx statuscode et gérer le côté client de la redirection.

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

Remplacer 400 avec le code de statut que vous voulez gérer. Comme déjà mentionné 401 Unauthorized pourrait être une bonne idée. Je utilise l 400 car il est très peu spécifique et je peux utiliser le 401 pour des cas plus spécifiques (comme les fausses informations de connexion). Donc, au lieu de rediriger directement votre backend devrait retourner un 4xx code d'erreur lorsque la session a expiré et que vous gérez le côté client de la redirection. Fonctionne parfaitement pour moi même avec des cadres comme backbone.js


17
2017-10-27 13:31



Ce problème peut apparaître puis en utilisant la méthode ASP.NET MVC RedirectToAction. Pour empêcher la forme d'afficher la réponse dans div, vous pouvez simplement faire un certain type de filtre de réponse ajax pour les réponses entrantes avec $ .ajaxSetup. Si la réponse contient une redirection MVC, vous pouvez évaluer cette expression du côté JS. Exemple de code pour JS ci-dessous:

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

Si les données sont: "window.location = '/ Acount / Login'" Le filtre ci-dessus va attraper cela et évaluer pour faire la redirection au lieu de laisser les données à afficher.


17
2018-01-06 01:33