Comment empêcher les requêtes ajax de suivre les redirections à l'aide de jQuery

103

J'utilise les fonctions jQuery ajax pour accéder à un service Web, mais le serveur, au lieu de renvoyer une réponse avec un code d'état décrivant un problème, la requête est redirigée vers une page avec un en-tête 200, décrivant le problème. Je ne peux pas apporter de modifications à cela, donc je dois le résoudre sur le client d'une manière ou d'une autre.

Exemple: une demande va à une URL qui n'est pas trouvée, donc je reçois une redirection 302 vers un autre emplacement. Une nouvelle demande est envoyée et je reçois un 200 OK, empêchant ainsi le rappel d'erreur de se déclencher.

Existe-t-il un moyen d'empêcher la demande ajax de suivre les redirections et d'appeler à la place un rappel, de préférence la méthode d'erreur. Sinon, est-il possible de détecter si une redirection s'est produite dans le client?

Jørgen
la source
8
Il est étrange que votre backend rapporte 302 pour un non trouvé, au lieu d'un 404.
Jake Feasel
Double
utilisateur

Réponses:

96

Je trouve votre question intéressante, mais le problème dans son ensemble me semble plutôt un malentendu. Au moins, je vais essayer d'expliquer ma compréhension du problème.

La redirection silencieuse (transparente) fait partie de la XMLHttpRequestspécification (voir ici notamment les mots "... suivez la redirection de manière transparente ..."). La norme mentionne uniquement que l'agent utilisateur (le navigateur Web) peut empêcher ou notifier certains types de redirections automatiques, mais cela n'en fait pas partie XMLHttpRequest. Cela fait partie de la configuration du client HTTP (configuration du système d'exploitation) ou de la configuration du navigateur Web. jQuery.ajaxVous ne pouvez donc pas avoir d'option permettant d'empêcher la redirection.

Vous pouvez voir que la redirection HTTP fait partie du protocole HTTP et non de XMLHttpRequest. C'est donc à un autre niveau d'abstraction ou de la pile réseau. Par exemple, les données du XMLHttpRequestpeuvent être récupérées à partir du proxy HTTP ou du cache du navigateur local, et cela fait partie du protocole HTTP. La plupart du temps, le serveur qui fournit les données et non le client peut influencer la mise en cache.

Vous pouvez comparer l'exigence de votre question avec l'exigence d'empêcher le changement d'adresse IP du serveur Web ou le changement de la route IP pendant la communication. Toutes les choses peuvent être intéressantes dans certains scénarios, mais il y a des parties d' un autre niveau de la pile de communication et ne peuvent pas être gérées par jQuery.ajaxou XMLHttpRequest.

La XMLHttpRequestnorme dit que la configuration du client peut avoir des options qui empêchent la redirection. Dans le cas du "monde Microsoft", que je connais mieux, vous pouvez consulter fonction WinHttpSetOption qui peut être utilisée pour définir l' WINHTTP_OPTION_DISABLE_FEATUREoption avec la WINHTTP_DISABLE_REDIRECTSvaleur. Une autre façon est l'utilisation de l' WINHTTP_OPTION_REDIRECT_POLICYoption avec la WINHTTP_OPTION_REDIRECT_POLICY_NEVERvaleur. Une autre fonctionnalité que l'on peut utiliser dans Windows est la fonction WinHttpSetStatusCallback qui peut définir la fonction de rappel reçu des notifications comme WINHTTP_CALLBACK_FLAG_REDIRECT.

Il est donc possible d'implémenter vos exigences en général, mais la solution ne sera probablement pas indépendante du système d'exploitation ou du navigateur Web et ne sera pas au niveau de jQuery.ajax ou XMLHttpRequest.

Oleg
la source
2
Excellente réponse avec une grande perspicacité! J'ai une certaine expérience avec curl, pour laquelle vous pouvez définir des indicateurs similaires à ceux que vous mentionnez. J'espérais que de telles instructions pourraient être transmises au navigateur, mais d'après votre réponse, je comprends que le comportement attendu serait de suivre les redirections comme si la demande initiale était envoyée à l'emplacement attribué par le serveur.
Jørgen
@ Jørgen: Vous êtes les bienvenus! Dans la plupart des scénarios, la redirection n'est pas du tout un problème. Vous ne décrivez pas le contexte dans lequel vous utilisez jQuery.ajax et si vous avez un contrôle total sur le serveur Web auquel vous envoyez la demande. Il est donc difficile de vous donner d'autres conseils qui peuvent vraiment résoudre votre problème.
Oleg
Je ne contrôle pas le côté serveur, j'en ai peur. Je suppose que ma meilleure option est d'analyser ou de valider le contenu de la réponse.
Jørgen
@ Jørgen: Pourquoi la redirection est-elle un problème dans votre cas? Si le serveur a déplacé une page sur un autre emplacement de manière temporaire ou permanente, il peut rediriger votre demande d'origine vers le nouvel emplacement. Ce sera absolument OK. Un administrateur peut configurer le serveur Web pour qu'il effectue la redirection, par exemple pendant l'opération de restauration sur le serveur ou tout autre travail d'assistance. Si vous demandez au serveur par URL ayant DNS, l'administrateur peut changer le mappage IP vers un autre serveur. Il peut effectuer une redirection HTTP de la même manière que la reconfiguration DNS. Quel est votre problème?
Oleg
Mon problème est que la redirection va vers une page d'erreur générique. Je sais que le serveur devrait être configuré différemment, mais pour l'instant, je devrai trouver une solution à cette situation telle qu'elle est.
Jørgen
23

Je ne crois pas que ce soit possible. La bibliothèque sous-jacente (XHR) effectue la nouvelle demande de manière transparente. Cela étant dit, ce que j'ai fait dans ces situations (généralement un type d'accord de délai d'expiration de session qui m'amène à une page de connexion) est de renvoyer un en-tête de réponse personnalisé. J'ai également configuré un gestionnaire ajax global qui vérifie la présence de cet en-tête et répond de manière appropriée lorsqu'il est présent (par exemple, en redirigeant la page entière vers l'écran de connexion).

Si vous êtes intéressé, voici le code jQuery que je dois surveiller pour cet en-tête personnalisé:

/* redirects main window when AJAX request indicates that the session has expired on the backend. */
function checkSession(event, xhr, ajaxOptions)
{
    if (xhr.readyState == 4)
    {
        if(xhr.getResponseHeader("Login-Screen") != null && xhr.getResponseHeader("Login-Screen").length)
        {
            window.location.href='sessionExpired.html'; //whatever
        }
    }
}

$(document).ajaxComplete(checkSession)
Jake Feasel
la source
1
Merci, je suppose que je vais devoir me contenter d'une approche similaire, en analysant la réponse.
Jørgen
11

J'ai trouvé une fonction pour vérifier si votre appel a été redirigé. C'est xhr.state (): s'il est "rejeté" alors une redirection s'est produite.

Exemple avec rappel de réussite:

request.success(function(data, textStatus, xhr)
{
    if(xhr.state() == "resolved")
    {
        //no redirection
    }
    if(xhr.state() == "rejected")
    {
        //redirection
    }
});

Exemple avec rappel d'erreur:

request.error(function(xhr, textStatus)
{
    if (xhr.state() == "rejected")
    {
        //redirection
        location.href = "loginpage";
    } else
    {
        //some other error happened
        alert("error");
    }
});
Takman
la source
1
Le conseil dans votre réponse nous a vraiment aidés à faire fonctionner les choses. Pour des notes qui aideraient les autres, nous avons WebSphere Portal dont la sécurité est intégrée à SiteMinder. Nous avons un portlet qui a besoin d'appels Ajax à une URL de ressource et lorsqu'il expire, les redirections transparentes se produisent, mais nous ne savons pas comment rediriger vers la page de connexion. Vérifier que jqXHR.state () était "rejeté" a sûrement aidé. Merci encore une fois.
Uresh Kuruhuri
Excellente trouvaille, mais il peut y avoir des faux positifs car l'état "rejeté" peut être déclenché même si une promesse est rejetée et pas seulement lors de la redirection. jQuery explique quand l'état "rejeté" est envoyé api.jquery.com/deferred.state
Nitin
1

Je ne peux pas ajouter à la sagesse perspicace des codeurs précédents qui ont répondu, mais j'ajouterai un cas spécifique que d'autres trouveront utile de connaître.

Je suis tombé sur cette redirection silencieuse 302 dans le contexte de SharePoint. J'ai un code client Javascript simple qui envoie un ping à un sous-site SharePoint, et s'il reçoit une réponse HTTP 200, il se déplace vers ce site, viawindow.location . S'il reçoit autre chose, il avertit l'utilisateur que le site n'existe pas.

Toutefois, dans le cas où le site existe mais que l'utilisateur n'a pas l'autorisation, SharePoint redirige silencieusement vers une page AccessDenied.aspx. SharePoint a déjà effectué la négociation d'authentification HTTP 401 au niveau du serveur / de la batterie de serveurs - l'utilisateur a accès à SharePoint. Mais l'accès au sous-site est géré, je suppose, à l'aide d'indicateurs de base de données. La redirection silencieuse contourne ma clause "else", donc je ne peux pas lancer ma propre erreur. Dans mon cas, ce n'est pas un spectacle - c'est un comportement prévisible cohérent. Mais c'était un peu surprenant, et j'ai appris quelque chose sur les requêtes HTTP dans le processus!

Chris van Hasselt
la source
1

J'étais intéressé par la même chose et je n'ai pas pu trouver la state()méthode mentionnée par Takman et j'ai creusé un peu pour moi-même. Pour le bien des gens qui viennent ici à la recherche d'une réponse, voici mes conclusions:

Comme indiqué plusieurs fois, vous ne pouvez pas empêcher les redirections, mais vous pouvez les détecter. Selon MDN , vous pouvez utiliser responseURLla XMLHttpRequestObject, qui contiendra l'URL finale la réponse est venue, après toutes les redirections. Le seul inconvénient est qu'il n'est pas pris en charge par Internet Explorer (Edge l'a). Puisque le xhr/ jqXHRpassé dans la fonction success/ donede jquery est une extension du réel XMLHttpRequest, il devrait également y être disponible.

Rialgar
la source
0

Je suppose que vous recevez une réponse 200 car la deuxième fois il n'y a pas de redirection, car la page 404 n'expire pas, elle est enregistrée dans le cache. C'est-à-dire que la deuxième fois que le navigateur vous donne la page dans le cache. Il y a une propriété "cache" dans la jquery ajax. http://api.jquery.com/jQuery.ajax/

Vous devriez l'écrire sur "false"

netadictos
la source
0

Bien qu'il ne soit pas possible de désactiver la redirection d'emplacement suivante dans XmlHttpRequests , c'est lors de l'utilisation de fetch () :

fetch('url', {redirect: manual});
cweiske
la source
Fetch ne vous dira pas quelle était l'URL redirigée. Vous pouvez désactiver le comportement, mais "redirect: manual" n'est pas destiné à permettre à l'utilisateur de gérer lui-même la redirection avec le nom intents.
lcjury
-2

Je ne sais pas si cela s'appliquera dans votre cas, mais vous pouvez écrire du code pour répondre à des codes d'état spécifiques dans la fonction AJAX -

$.ajax({
    url: '/admin/secret/data',
    type: 'POST',
    contentType: 'application/json; charset=utf-8',
    statusCode: {
        200: function (data) {
            alert('302: Occurred');
            // Bind the JSON data to the UI
        },
        401: function (data) {
            alert('401: Occurred');
            // Handle the 401 error here.
        }
    }
});
ipr101
la source
9
Je ne pense pas que cela s'applique parce que OP a dit qu'il obtenait toujours un code de statut 200 en raison de la redirection en arrière-plan ..
Oui Barry
-4

Dans les en-têtes de requête, dans le cas d'une requête ajax, vous aurez les éléments suivants

X-Requested-With    XMLHttpRequest

Par ce critère côté serveur, vous pouvez filtrer les demandes.

Gfox
la source
Il voulait vérifier la réponse et non la demande (sur laquelle il a déjà le contrôle)
Oui Barry
Peut-être que Gfox a suggéré que pour les requêtes ajax renvoyant 3xx sont inutiles, vous pouvez filtrer ce type de requête et renvoyer 403 par exemple. lorsque vous avez un site Web qui sert des pages qui nécessitent une authentification par formulaire et que ces pages font également des appels ajax et que vous souhaitez mettre la logique d'autorisation au même endroit, alors pour moi, c'est une réponse valable.
maciejW