web-dev-qa-db-fra.com

Traiter la réponse HTTP 302 du proxy dans angularjs

J'ai un proxy inverse qui vérifie l'authentification globale de plusieurs applications. Lorsque l'utilisateur est déconnecté mais tente toujours d'utiliser mon application, le proxy envoie une réponse 302: 

HTTP/1.1 302 Found
Date: Wed, 11 Sep 2013 09:05:34 GMT
Cache-Control: no-store
Location: https://other.url.com/globalLoginPage.html
Content-Length: 561
Content-Type: text/html; charset=iso-8859-1
Via: 1.1 my-proxy.com
Connection: Keep-Alive

Dans angularJs, le rappel d'erreur est appelé mais les en-têtes de réponse sont vides, le statut est 0 et les données sont une chaîne vide. Il semble donc que je ne puisse vraiment rien faire pour gérer la réponse ...
J'ai vu plusieurs questions sur le sujet et je ne comprends toujours pas ce qui se passe (CORS à cause du proxy ou d'un domaine différent dans l'emplacement?, 302 comportement du navigateur?).
Il y a notamment cette partie de réponse ( https://stackoverflow.com/a/17903447/1093925 ): 

Remarque: Si votre serveur définit un code de réponse de 301 ou 302, vous ne pourrez pas obtenir l'en-tête Location car il sera suivi automatiquement et de manière transparente par l'objet XMLHttpRequest.

Qu'en est-il de cet objet XMLHttpRequest?
Dans une très ancienne version de Chrome (je ne peux pas utiliser une version plus récente), je peux voir une demande correspondante dans le panneau de réseau, mais elle semble échouer car il n’ya pas de réponse.
Dans la dernière version de firefox, rien ne se passe. 

Puis-je faire quelque chose à ce sujet, étant donné que je ne peux pas modifier la configuration et la réponse du proxy?

Mettre à jour:
J'ai rejoué mon scénario aujourd'hui et, grâce à une nouvelle version de Firebug, j'ai pu obtenir plus de détails sur ce qui se passe.
Je n’étais pas loin de la réponse à ma question: Politique de domaine croisé.
Puisqu'il s'agit d'une requête HTTP faite par mon application, le navigateur refuse le XMLHttpRequest suivant (qui dans l'application ressemble à la même requête). D'où l'erreur et la réponse vide.
Donc, je pense qu'il n'y a rien de spécial à faire

32
D0m3

J'ai eu le même problème dans mon application . Vous ne pouvez pas vraiment "attraper" une réponse de redirection 302. Le navigateur le détecte avant qu'Angular ait la main dessus. En fait, lorsque vous recevez votre réponse, il est déjà trop tard.

La mauvaise nouvelle: ce n'est pas un problème dans la plate-forme angulaire. XmlHttpRequest ne prend pas en charge ce type de comportement et le navigateur agit avant que vous ne puissiez rien faire à ce sujet . Reference: Empêcher la redirection de Xmlhttprequest

La bonne nouvelle: il existe de nombreuses façons de contourner ce problème, en interceptant la réponse - et de trouver le moyen de reconnaître que c'est votre adorable 302. C'est un bidouillage, mais c'est le mieux que vous puissiez faire pour le moment.

Alors. Par exemple, dans mon application, la redirection revenait à la page login.html de l'application, et dans l'application, j'ai reçu la réponse avec un statut 200 et les données de la réponse étaient le contenu de mes identifiants. .html page . Donc, dans mon intercepteur, j'ai vérifié si le résultat était une chaîne (généralement pas! so- no prob d'efficience ..) et si tel est le cas, il s'agit de ma page de connexion.html. De cette façon, je pourrais attraper la redirection et le gérer à ma façon.

yourApp.factory('redirectInterceptor', ['$location', '$q', function($location, $q) {
    return function(promise) {
        promise.then(
            function(response) {
                if (typeof response.data === 'string') {
                    if (response.data.indexOf instanceof Function &&
                        response.data.indexOf('<html id="ng-app" ng-app="loginApp">') != -1) {
                        $location.path("/logout");
                        window.location = url + "logout"; // just in case
                    }
                }
                return response;
            },
            function(response) {
                return $q.reject(response);
            }
        );
        return promise;
    };
}]);

Insérez ensuite cet intercepteur dans votre application. quelque chose comme ça:

$httpProvider.responseInterceptors.Push('redirectInterceptor');

bonne chance.

40
Ofer Segev

Votre navigateur 302 -Redirect est traité directement par le navigateur et vous ne pouvez rien y faire directement. Vous pouvez cependant utiliser une httpInterceptor pour vous aider. Vous devrez inclure $httpProvider dans votre liste d'identifiant d'application, puis quelque part dans votre fonction de configuration, mettez-y une référence comme ceci:

$httpProvider.responseInterceptors.Push('HttpInterceptor');

Un exemple d'intercepteur ressemble à ceci:

window.angular.module('HttpInterceptor', [])
.factory('HttpInterceptor', ['$q', '$injector',
    function($q, $injector) {
        'use strict';

        return function(promise) {
            return promise.then(success, error);
        };

        function success(response) {
            return response;
        }

        function error(response) {
            var isAuthRequest = (response.config.url.indexOf('/v1/rest/auth') !== -1);

            //if we are on the authenticating don't open the redirect dialog
            if (isAuthRequest) {
                return $q.reject(response);
            }

            //open dialog and return rejected promise
            openErrorDialog(response);
            return $q.reject(response);
        }

        function openErrorDialog(response) {
            $injector.get('$dialog').dialog({
                backdropFade: true,
                dialogFade: true,
                dialogClass: 'modal newCustomerModal',
                resolve: {
                    errorData: function() {
                        return response.data;
                    },
                    errorStatus: function() {
                        return response.status;
                    }
                }
            })
            .open('/views/error-dialog-partial.htm',
                'errorDialogController')
            .then(function(response) {
                if (response) {
                    window.location = '/';
                }
            });
        }
    }
]);
3
MBielski

J'avais un problème très similaire et examinais la solution fournie par Ofer Segev, mais vérifier le contenu de la réponse pour voir si elle correspondait à un fragment html d'une autre page me paraissait trop compliqué. Que se passe-t-il quand quelqu'un change de page? 

Heureusement, j'avais également le contrôle du backend. Au lieu de renvoyer un 302 (redirection), j'ai renvoyé un 403 (interdit) et transmis l'emplacement souhaité dans les en-têtes. Contrairement au 302, le 403 sera géré dans votre gestionnaire d'erreurs, où vous pourrez décider quoi faire. Voici mon gestionnaire résultant:

function ($scope, $http) {
    $http.get(_localAPIURL).then(function (response) {
            // do what I'd normally do
        }, function (response) {
        if (response.status == 403)
        {
            window.location.href = response.headers().location;
        }
        else
        {
            // handle the error
        }
1
plunkg

Comme indiqué ci-dessus, 302 réponses ne sont pas disponibles pour Angular, car xmlHttpRequest ne prend pas en charge le renvoi des redirections. le navigateur agit avant que vous puissiez faire quelque chose à ce sujet.

En plus du traitement des réponses de données personnalisées et des codes d’état prioritaires,
vous pouvez utiliser une solution plus générique et ajouter un en-tête de redirection personnalisé, tel que X-Redirect, et agir en conséquence. La valeur de X-Redirect devrait être l'URL vers laquelle vous voulez rediriger, par exemple https://other.url.com/globalLoginPage.html.

$http.get(orig_url).then(function(response) {
        // do stuff
    }, function (response) {
        var headers = response.headers();
        if ('x-redirect' in headers)
        {
            document.location.href = headers['x-redirect'];
        }
        return response;
    }
}

Pour une solution plus globale, vous pouvez également utiliser un intercepteur HTTP.

app.factory('myHttpInterceptor', function ($q, myCache) {
    return {
        request: function (config) {
            return config;
        },
        response: function(response){
            var headers = response.headers();
            if ('x-redirect' in headers)
            {
                document.location.href = headers['x-redirect'];
            }    
            return response;
        },
        responseError: function(rejection) {    
            switch(rejection.status)
            {
                case 302:
                    // this will not occur, use the custom header X-Redirect instead
                    break;
                case 403:
                    alert.error(rejection.data, 'Forbidden');
                    break;
            }
            return $q.reject(rejection);
        }
    };
});

Vous pouvez définir le côté serveur d'en-tête personnalisé.
Par exemple, si vous utilisez PHP Symfony:

$response = new Response('', 204);
$response->headers->set('X-Redirect', $this->generateUrl('other_url'));
return $response;
0
browniebytes