Handle HTTP 302 response from proxy in angularjs

前端 未结 5 1536
迷失自我
迷失自我 2020-11-28 06:19

I have a reverse proxy that checks global authentication for several applications. When the user is disconnected but still trying to use my application, the proxy sends a 30

相关标签:
5条回答
  • As discussed above, 302 responses are not available to Angular as xmlHttpRequest does not support returning redirects; the browser acts before you can do anything about it.

    In addition to processing custom data responses and overriding status codes,
    you could use a more generic solution and add a custom redirect header, such as X-Redirect and act upon that. The value of X-Redirect should be the url you want to redirect to eg 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;
        }
    }
    

    For a more global solution, you could also use a HTTP Interceptor

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

    You can set the custom header server side.
    For example, if you are using PHP Symfony:

    $response = new Response('', 204);
    $response->headers->set('X-Redirect', $this->generateUrl('other_url'));
    return $response;
    
    0 讨论(0)
  • 2020-11-28 06:40

    Your 302 -Redirect is being handled directly by the browser and there is nothing you can do about it directly. You can, however, use an httpInterceptor to help you along. You'll need to include $httpProvider in your app DI list, and then somewhere in your config function put a reference to it like this:

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

    A sample interceptor looks like this:

    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 = '/';
                    }
                });
            }
        }
    ]);
    
    0 讨论(0)
  • 2020-11-28 06:45

    I had the same problem in my app. You can't really "catch" a 302 redirect response. The browser catches it before Angular get it's hand on it. so actually, when you do receive your response - it is already too late.

    The bad news: it's not a problem in the angular platform. The xmlHttpRequest do not support this kind of behaviour, and the browser act before you can do anything about it. reference: Prevent redirection of Xmlhttprequest

    The good news: there are many ways to bypass this problem, by intercepting the response- and find some way to recognize that it's your lovable 302. This is a hack, but it's the best you can do at the moment.

    So. For example, in my app, the redirect was back to the login.html page of the app, and in the app i got the response with a 200 status and the data of the response was the content of my login.html page. so in my interceptor, i checked if the result is a string (usually not! so- no efficiency prob..) and if so- checked if it's my login.html page. that way, i could catch the redirect and handle it my way.

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

    Then insert this interceptor to your app. something like this:

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

    good luck.

    0 讨论(0)
  • 2020-11-28 06:49

    Just for reference in case anyone still looks at this: you can also make the call that is expected to return a 302 form an Iframe. That way you stay on the page, and you can communicate with https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage

    0 讨论(0)
  • 2020-11-28 06:50

    I had a very similar issue, and considered the solution provided by Ofer Segev, but checking the content of the response to see if it matched an html fragment of another page just seemed too hacky to me. What happens when someone changes that page?

    Fortunately, I had control of the backend as well, so instead of returning a 302 (Redirect), I returned a 403 (Forbidden), and passed the desired location in the headers. Unlike the 302, the 403 will be handled in your error handler, where you can decide what to do next. Here was my resulting handler:

    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
            }
    
    0 讨论(0)
提交回复
热议问题