web-dev-qa-db-fra.com

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

J'utilise $.post() pour appeler un servlet à l'aide d'Ajax, puis le fragment HTML résultant pour remplacer un élément div 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 l'élément div par le contenu de la page de connexion, forçant ainsi 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?

1249
Elliot Vargas

La solution qui a finalement été mise en œuvre consistait à utiliser un wrapper pour la fonction de rappel de l'appel Ajax et, dans ce wrapper, il vérifiait l'existence d'un élément spécifique sur le fragment HTML renvoyé. Si l'élément est trouvé, l'encapsuleur exécute une redirection. Sinon, le wrapper a transféré l'appel vers 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 le code HTML dans un élément DIV que nous utilisons pour remplacer un élément de la page. De plus, nous n'avions besoin que de rediriger vers la page de connexion.

88
Elliot Vargas

J'ai lu cette question et mis en œuvre l'approche qui a été exposée concernant la définition du code d'état de la réponse sur 278 afin d'éviter que le navigateur ne traite les redirections de manière transparente. Même si cela a fonctionné, j'étais un peu mécontent car c'est un peu un bidouillage.

Après plus de recherches, j'ai abandonné cette approche et utilisé JSON . Dans ce cas, toutes les réponses aux demandes ajax ont le code d'état 200 et le corps de la réponse contient un objet JSON 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'exécute une requête ajax comportant 2 réponses possibles: une qui redirige le navigateur vers une nouvelle page et une qui remplace un formulaire HTML existant sur la page en cours par un nouveau. Le code jQuery pour faire cela ressemble à quelque chose comme:

$.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.

654
Steg

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

  1. Ajout d'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. Lier une fonction JavaScript à ajaxSuccess et vérifier si l'en-tête existe:

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

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

Cela me gêne vraiment, et si quelqu'un ici a une certaine "traction" dans le W3C, j'apprécierais que vous puissiez laisser le W3C savoir que nous devons vraiment gérer les codes 301 et 302 nous-mêmes ...! ;)

109
Thomas Hansen

J'aime la méthode de Timmerz avec une légère touche de citron. Si vous recevez un jour contentType of text/html lorsque vous attendezJSON, vous êtes probablement redirigé. Dans mon cas, je recharge simplement la page et celle-ci est redirigée vers la page de connexion. Oh, et vérifiez que l'état de jqXHR est 200, ce qui semble idiot, car vous êtes dans la fonction d'erreur, non? Sinon, les cas d'erreur 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();
    }

});
60
BrianY

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

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

Je voulais juste partager mon approche car cela pourrait peut-être aider quelqu'un:

En gros, j'ai inclus un module JavaScript qui gère les tâches d'authentification, telles que l'affichage du nom d'utilisateur et également ce cas, qui gère le redirection 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 en-tête 302 et un en-tête d’emplacement} à notre page de connexion.

Dans mon module JavaScript, mon approche initiale ressemblait à quelque chose comme

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

Le problème (comme beaucoup d’entre eux ont déjà été mentionnés) est que le navigateur gère lui-même la redirection. Par conséquent, mon rappel ajaxComplete n’a jamais été appelé, mais j’ai reçu la réponse de la page de connexion déjà redirigée qui était évidemment un status 200. Le problème: comment détecter si la réponse 200 réussie est votre page de connexion réelle ou simplement une autre page arbitraire?

La solution

Comme je ne pouvais pas capturer 302 réponses de redirection, j'ai ajouté un en-tête LoginPage sur ma page de connexion contenant l'URL de la page de connexion elle-même. Dans le module, j'écoute maintenant l'en-tête et fais une redirection:

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

... et ça marche comme un charme :). Vous vous demandez peut-être pourquoi j'ai inclus l'URL dans l'en-tête LoginPage ... enfin, fondamentalement, parce que je n'ai trouvé aucun moyen de déterminer l'URL de GET résultant de la redirection automatique d'emplacement à partir de l'objet xhr ...

32
Juri

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 cadre utilisé. L'indice reste inchangé - le traitement des problèmes liés aux échecs d'authentification lors de l'exécution demandes ajax)

L'approche présentée ci-dessous peut être appliquée à toutes les demandes ajax prêtes à l'emploi (si elles ne redéfinissent pas l'évènement beforeSend de manière évidente).

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

Avant que toute requête ajax ne soit exécutée, la méthode CheckPulse est invoquée (la méthode du contrôleur, qui 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 est inaccessible (protégée par l'attribut Authorize). Parce que la structure gère l'authentification, alors que le jeton expire, il attribue le statut http 302 à la réponse. Si vous ne voulez pas que votre navigateur traite la réponse 302 de manière transparente, récupérez-la dans Global.asax et changez le statut de la réponse - par exemple, 200 OK. De plus, ajoutez un en-tête, qui vous indique de traiter cette réponse de manière spéciale (ultérieurement 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 cet en-tête personnalisé. S'il est présent, la redirection complète vers la page de connexion doit être effectuée (dans mon cas, window.location est remplacé par l'url de la requête qui est gérée automatiquement par mon infrastructure).

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

Je pense qu'une meilleure façon de gérer cela consiste à exploiter les 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 demande est ajax. envoyer un en-tête de code de réponse 401
  2. Côté client: se 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és. Vous ne devriez pas non plus avoir à modifier vos appels ajax existants.

Edit: Selon le commentaire de @ Rob ci-dessous, 401 (le code d'état HTTP pour les erreurs d'authentification) devrait être l'indicateur. Voir 403 Interdit vs 401 Réponses HTTP non autorisées pour plus de détails. Cela dit, certains frameworks Web utilisent 403 à la fois pour les erreurs d’authentification ET d’autorisation - adaptez-les en conséquence. Merci Rob.

22
rynop

J'ai résolu ce problème comme ceci:

Ajoutez un middleware pour traiter la réponse. S'il s'agit d'une redirection pour une requête ajax, modifiez la réponse en 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

Ensuite, dans ajaxComplete, si la réponse contient une redirection, il doit s'agir d'une redirection; changez donc 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;
       }
   }
}
22
Tyr

La plupart des solutions fournies utilisent une solution de contournement, utilisant un en-tête supplémentaire ou un code HTTP peu approprié. Ces solutions fonctionneront probablement, mais vous semblerez un peu "hacky". J'ai mis au point une autre solution.

Nous utilisons WIF qui est configuré pour rediriger (passiveRedirectEnabled = "true") sur une réponse 401. La redirection est utile lors du traitement de requêtes normales mais ne fonctionne pas pour les requêtes AJAX (car les navigateurs n'exécutent pas la redirection 302 /). 

En utilisant le code suivant dans votre global.asax, vous pouvez désactiver la redirection pour les demandes AJAX:

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

Cela vous permet de renvoyer 401 réponses à des demandes AJAX, que votre javascript peut ensuite gérer en rechargeant la page. Recharger la page lancera un 401 qui sera traité par WIF (et WIF redirigera l'utilisateur vers la page de connexion).

Un exemple javascript pour gérer les erreurs 401:

$(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

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

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

Remplacez 400 par le code de statut que vous souhaitez gérer. Comme déjà mentionné, 401 Unauthorized pourrait être une bonne idée. J'utilise le 400 car il est très peu spécifique et je peux utiliser le 401 pour des cas plus spécifiques (comme des identifiants de connexion erronés). Ainsi, au lieu de rediriger directement votre serveur, vous devriez renvoyer un 4xx code d'erreur lorsque la session a expiré et que vous gérez la redirection côté client. Fonctionne parfaitement pour moi même avec des frameworks comme backbone.js

17
morten.c

Ce problème peut alors apparaître à l'aide de la méthode ASP.NET MVC RedirectToAction. Pour empêcher le formulaire d’afficher la réponse dans div, vous pouvez simplement utiliser une sorte 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 data est: "window.location = '/ Acount/Login" " le filtre ci-dessus le capturera et évaluera la redirection au lieu de laisser les données s'afficher.

17

Voici ce que Vladimir Prudnikov et Thomas Hansen ont dit:

  • Changez votre code côté serveur pour détecter si c'est un XHR. Si c'est le cas, définissez le code de réponse de la redirection sur 278 . Dans Django:
   if request.is_ajax():
      response.status_code = 278

Cela fait que le navigateur considère la réponse comme un succès et la transmet à votre Javascript.

  • Dans votre JS, assurez-vous que le formulaire est soumis via Ajax, vérifiez le code de réponse et redirigez-le si nécessaire:
$('#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

J'ai une solution simple qui fonctionne pour moi, aucun changement de code serveur requis ... ajoutez juste une cuillère à café de noix de muscade ...

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

Je vérifie la présence de balises html, mais vous pouvez modifier indexOf pour rechercher une chaîne unique dans votre page de connexion ...

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

Essayer

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

Mettez-le sur la page de connexion. S'il a été chargé dans un div sur la page principale, il sera redirigé jusqu'à la page de connexion. "#site" est l'identifiant d'un div qui se trouve sur toutes les pages sauf la page de connexion.

12
podeig

Bien que les réponses semblent fonctionner pour les personnes si vous utilisez Spring Security, j'ai trouvé l'extension de LoginUrlAuthenticationEntryPoint et l'ajout d'un code spécifique pour gérer AJAX plus robuste. La plupart des exemples intercept all ne redirigent pas uniquement les échecs d'authentification. Ce n'était pas souhaitable pour le projet sur lequel je travaille. Vous pouvez également trouver nécessaire d'étendre ExceptionTranslationFilter et de remplacer la méthode "sendStartAuthentication" pour supprimer l'étape de mise en cache si vous ne souhaitez pas que la demande AJAX ayant échoué soit mise en cache.

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

Sources: 1 , 2

11
John

J'ai résolu ce problème en mettant ce qui suit dans ma page login.php.

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

Certains pourraient trouver le ci-dessous utile:

Je souhaitais que les clients soient redirigés vers la page de connexion pour toute action de repos envoyée sans jeton d'autorisation. Puisque toutes mes actions-rest sont basées sur Ajax, j'avais besoin d'un bon moyen générique pour rediriger vers la page de connexion au lieu de gérer la fonction de réussite Ajax.

Voici ce que j'ai fait:

Sur toute requête Ajax, mon serveur renvoie une réponse Json 200 "NEED TO AUTHENTICATE" (si le client doit s'authentifier).

Exemple simple en Java (côté serveur):

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

Dans mon javascript, j'ai ajouté le code suivant:

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

Et c'est à peu près tout.

7
Tomer

Permettez-moi de citer à nouveau le problème décrit par @Steg

J'ai eu un problème similaire au vôtre. J'exécute une demande ajax qui a 2 réponses possibles: une qui redirige le navigateur vers une nouvelle page et celui qui remplace un formulaire HTML existant sur la page en cours par un nouveau un.

IMHO c'est un véritable défi et devra être officiellement étendu aux standards HTTP actuels.

Je crois que la nouvelle norme Http consistera à utiliser un nouveau code d'état . Signifiant: actuellement 301/302 indique au navigateur d'aller chercher le contenu de la requête this dans une nouvelle location.

Dans la norme étendue, il sera dit que si la réponse status: 308 (juste un exemple), le navigateur doit rediriger la page principale vers la location fournie.

Cela étant dit; Je suis enclin à imiter déjà ce comportement future, et donc lorsqu'un document.redirect est nécessaire, le serveur répond en tant que:

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

Lorsque JS obtient le "status: 204", il vérifie l'existence de l'en-tête x-status: 308 et effectue un document.redirect vers la page fournie dans l'en-tête location.

Cela a-t-il un sens pour vous?

7
Chaim Klar

dans la servlet, vous devez mettre response.setStatus(response.SC_MOVED_PERMANENTLY);pour envoyer le statut '301' xmlHttp dont vous avez besoin pour une redirection ...

et dans la fonction $ .ajax, vous ne devriez pas utiliser la fonction .toString() ..., juste

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

le problème est que ce n’est pas très flexible, vous ne pouvez pas décider où vous voulez rediriger ..

rediriger par les servlets devrait être le meilleur moyen. mais je ne trouve toujours pas la bonne façon de le faire.

5
Juan Toro

Enfin, je résous le problème en ajoutant un HTTP Header personnalisé. Juste avant la réponse pour chaque demande côté serveur, j'ajoute l'URL demandée actuelle à l'en-tête de la réponse.

Mon type d'application sur le serveur est Asp.Net MVC et il est bien placé pour le faire. dans Global.asax, j'ai implémenté l'événement Application_EndRequest pour:

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

    }

Cela fonctionne parfaitement pour moi! Maintenant, dans chaque réponse de la JQuery$.post, j'ai la url demandée ainsi que d'autres en-têtes de réponse qui résultent de la méthode POST selon le statut 302, 303, ....

un autre point important est qu’il n’est pas nécessaire de modifier le code côté serveur ni côté client.

et le suivant est la possibilité d’avoir accès aux autres informations post-action telles que les erreurs, les messages et ..., de cette manière.

J'ai posté ceci, peut-être aider quelqu'un :)

5
Ali Adlavaran

Je n'ai eu aucun succès avec la solution d'en-tête - ils n'ont jamais été repris dans ma méthode ajaxSuccess/ajaxComplete J'ai utilisé la réponse de Steg avec la réponse personnalisée, mais j'ai modifié le côté JS. J'ai configuré une méthode que j'appelle dans chaque fonction afin de pouvoir utiliser les méthodes standard $.get et $.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);
    }
}

Exemple d'utilisation en cours ...

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

Je voulais juste m'accrocher à toutes les demandes ajax pour la page entière. @SuperG m'a lancé. Voici ce que j'ai fini avec:

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

Je voulais vérifier spécifiquement certains codes de statut http sur lesquels baser ma décision. Cependant, vous pouvez simplement vous connecter à ajaxError pour obtenir autre chose que le succès (200 seulement peut-être?) J'aurais pu juste écrire:

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

Si vous souhaitez également transmettre les valeurs, vous pouvez également définir les variables de session et accéder à Par exemple: Dans votre jsp, vous pouvez écrire 

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

Et puis vous pouvez stocker cette valeur temporaire dans votre variable javascript et jouer 

5
karthik339

Je rencontrais ce problème sur une application Django avec laquelle je bricolais (disclaimer: je bricole pour apprendre et je ne suis en aucun cas un expert). Ce que je voulais faire, c'est utiliser jQuery ajax pour envoyer une demande DELETE à une ressource, la supprimer côté serveur, puis renvoyer une redirection vers (en gros) la page d'accueil. Lorsque j'ai envoyé HttpResponseRedirect('/the-redirect/') à partir du script python, la méthode ajax de jQuery en recevait 200 au lieu de 302. J'ai donc envoyé une réponse de 300 avec:

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

Ensuite, j'ai envoyé/géré la demande sur le client avec jQuery.ajax comme ceci:

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

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

Peut-être qu’utiliser 300 n’est pas «correct», mais au moins cela a fonctionné exactement comme je le voulais.

PS: l'édition de la version mobile de SO était très pénible. Stupid ISP a bien répondu à ma demande d'annulation de service! 

4
Benny Jobigan

Vous pouvez également accrocher le prototype d'envoi XMLHttpRequest. Cela fonctionnera pour tous les envois (jQuery/dojo/etc) avec un seul gestionnaire.

J'ai écrit ce code pour gérer une erreur de 500 pages expirées, mais cela devrait tout aussi bien fonctionner pour intercepter une redirection de 200. Préparez l'entrée de wikipedia sur XMLHttpRequest onreadystatechange sur la signification de 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);
}
4
Curtis Yallop

De plus, vous voudrez probablement rediriger l'utilisateur vers l'URL d'en-tête donnée. Donc finalement ça va ressembler à ça:

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

UPD: Opps. Avoir la même tâche, mais ça ne marche pas. Faire ce genre de choses. Je te montrerai la solution quand je la trouverai.

1
Vladimir Prudnikov

J'ai eu une solution de travail en utilisant les réponses de @John et @Arpad lien et @RobWinch lien

J'utilise Spring Security 3.2.9 et jQuery 1.10.2.

Étendez la classe de Spring pour ne générer que la réponse 4XX à partir de requêtes AJAX:

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>

Dans mes JSP, ajoutez un gestionnaire d’erreur global AJAX, comme indiqué ici

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

Supprimez également les gestionnaires d’erreurs existants des appels AJAX dans les pages JSP:

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

J'espère que ça aide les autres.

Update1 J'ai constaté que je devais ajouter l'option (toujours-use-default-target = "true") à la configuration de connexion au formulaire . Cela était nécessaire après un AJAX La demande est redirigée vers la page de connexion (en raison de l’expiration de la session), Spring se souvient de la demande AJAX précédente et y était automatiquement redirigé après la connexion. Cela provoque l'affichage du code JSON renvoyé sur la page du navigateur. Bien sûr, pas ce que je veux.

Update2 Au lieu d'utiliser always-use-default-target="true", utilisez @RobWinch, exemple de blocage des demandes AJAX à partir de requstCache. Cela permet aux liens normaux d'être redirigés vers leur cible d'origine après la connexion, mais AJAX va à la page d'accueil après la connexion.

1
Darren Parker