Подтвердить что ты не робот

Обработка ответа HTTP 302 от прокси-сервера в angularjs

У меня есть обратный прокси-сервер, который проверяет глобальную аутентификацию для нескольких приложений. Когда пользователь отключен, но все еще пытается использовать мое приложение, прокси отправляет ответ 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

В angularJs вызывается обратный вызов ошибки, но заголовки ответа пустые, статус 0, а данные - пустая строка. Поэтому кажется, что я действительно не могу ничего сделать, чтобы справиться с ответом...
Я видел несколько вопросов по этому вопросу, и я до сих пор не понимаю, что происходит (CORS из-за прокси или другого домена в местоположении?, Поведение браузера 302?).
В частности, есть эта часть ответа (qaru.site/info/210021/...):

Примечание. Если ваш сервер устанавливает код ответа 301 или 302, вы не сможете получить заголовок Location, так как он будет автоматически и прозрачно следовать за объектом XMLHttpRequest.

Как насчет этого объекта XMLHttpRequest?
В очень старой версии Chrome (не могу использовать более новую версию) я вижу, что соответствующий запрос на панели сети, но, похоже, не работает, поскольку ответа нет.
В последней версии firefox ничего не происходит.

Могу ли я что-нибудь сделать с этим, так как я не могу изменить конфигурацию прокси-сервера и ответ?

Update:
Сегодня я переиграл свой сценарий, и благодаря новой версии firebug я смог получить более подробную информацию о том, что происходит.
Я был недалеко от андерсея в моем вопросе: перекрестная политика домена.
Поскольку это HTTP-запрос, сделанный моим приложением, браузер отказывает в следующем XMLHttpRequest (которое в приложении похоже на тот же запрос). Отсюда ошибка и пустой ответ.
Поэтому я думаю, что я ничего особенного не могу с этим сделать

4b9b3361

Ответ 1

У меня была такая же проблема в моем приложении. Вы не можете "поймать" ответ перенаправления 302. Браузер ловит его перед тем, как Angular получит его. так что на самом деле, когда вы получите ответ - уже слишком поздно.

Плохая новость: это не проблема на платформе Angular. XmlHttpRequest не поддерживает такое поведение, и браузер действует, прежде чем вы сможете что-либо сделать. ссылка: Предотвратить перенаправление Xmlhttprequest

Хорошая новость: есть много способов обойти эту проблему, перехватив ответ и найти способ распознать, что это ваш любимый 302. Это взломать, но это лучший из вас может сделать в данный момент.

Так. Например, в моем приложении перенаправление было возвращено на страницу login.html приложения, и в приложении я получил ответ с статусом 200, и данные ответа были содержимым моей страницы login.html. поэтому в моем перехватчике я проверял, является ли результат строкой (обычно это не так, если нет проблемы с эффективностью), и если это так проверено, если это моя страница login.html. таким образом, я мог поймать перенаправление и обработать его по-своему.

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

Затем вставьте этот перехватчик в свое приложение. что-то вроде этого:

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

удачи.

Ответ 2

Ваш 302-Redirect обрабатывается непосредственно браузером, и вы ничего не можете с ним поделать напрямую. Однако вы можете использовать httpInterceptor, чтобы помочь вам. Вам нужно будет включить $httpProvider в список DI вашего приложения, а затем где-то в вашей функции конфигурации поместите ссылку на него следующим образом:

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

Примерный перехватчик выглядит следующим образом:

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

У меня была очень похожая проблема, и я рассмотрел решение, предоставленное Ofer Segev, но проверяя содержание ответа, чтобы увидеть, соответствует ли он html-фрагмент другой страницы, просто показалось мне слишком хриплым. Что происходит, когда кто-то меняет эту страницу?

К счастью, я также имел контроль над бэкэнд, поэтому вместо того, чтобы возвращать 302 (перенаправление), я вернул 403 (Запрещено) и передал нужное место в заголовках. В отличие от 302, 403 будет обрабатываться в вашем обработчике ошибок, где вы можете решить, что делать дальше. Вот мой обработчик:

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
        }

Ответ 4

Как обсуждалось выше, 302 ответа не доступны для Angular, поскольку xmlHttpRequest не поддерживает возвратные перенаправления; браузер действует, прежде чем вы сможете что-либо сделать.

В дополнение к обработке пользовательских ответов данных и переопределению кодов состояния,
вы можете использовать более общее решение и добавить настраиваемый заголовок перенаправления, такой как X-Redirect и действовать по этому поводу. Значение X-Redirect должно быть URL-адресом, который вы хотите перенаправить, например, 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;
    }
}

Для более глобального решения вы также можете использовать 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);
        }
    };
});

Вы можете настроить пользовательский сервер заголовка.
Например, если вы используете PHP Symfony:

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