Manipulador de erro global que captura apenas promessas “não manipuladas”

9

Eu tenho um manipulador de erros global para meu aplicativo angular, que é escrito como $http interceptor , mas gostaria de dar um passo adiante. O que eu gostaria é que cada chamada de $http que falha (seja rejeitada), qualquer consumidor "acorrentado" da promessa deve primeiro tentar resolver o erro, e se ele ainda não estiver resolvido (não detectado), ENTÃO eu como o manipulador de erro global para assumir.

Caso de uso, meu manipulador de erro global mostra um grunhido " alert box " na parte superior da tela. Mas eu tenho um par de modais que aparecem, e eu ligo explicitamente os erros lá, mostrando uma mensagem de erro no próprio modal. Portanto, essencialmente, esse controlador modal deve marcar a promessa rejeitada como "manipulada". Mas como o interceptador sempre parece ser o primeiro a ser executado em um $http error , não consigo descobrir uma maneira de fazê-lo.

Aqui está o meu código de interceptador:

angular.module("globalErrors", ['angular-growl', 'ngAnimate'])
    .factory("myHttpInterceptor", ['$q', '$log', '$location', '$rootScope', 'growl', 'growlMessages',
        function ($q, $log, $location, $rootScope, growl, growlMessages) {
            var numLoading = 0;
            return {
                request: function (config) {
                    if (config.showLoader !== false) {
                        numLoading++;
                        $rootScope.loading = true;
                    }
                    return config || $q.when(config)
                },
                response: function (response) {
                    if (response.config.showLoader !== false) {
                        numLoading--;
                        $rootScope.loading = numLoading > 0;
                    }
                    if(growlMessages.getAllMessages().length) { // clear messages on next success XHR
                        growlMessages.destroyAllMessages();
                    }
                    return response || $q.when(response);
                },
                responseError: function (rejection) {
                    //$log.debug("error with status " + rejection.status + " and data: " + rejection.data['message']);
                    numLoading--;
                    $rootScope.loading = numLoading > 0;
                    switch (rejection.status) {
                        case 401:
                            document.location = "/auth/login";
                            growl.error("You are not logged in!");
                            break;
                        case 403:
                            growl.error("You don't have the right to do this: " + rejection.data);
                            break;
                        case 0:
                            growl.error("No connection, internet is down?");
                            break;
                        default:
                            if(!rejection.handled) {
                                if (rejection.data && rejection.data['message']) {
                                    var mes = rejection.data['message'];
                                    if (rejection.data.errors) {
                                        for (var k in rejection.data.errors) {
                                            mes += "<br/>" + rejection.data.errors[k];
                                        }
                                    }
                                    growl.error("" + mes);
                                } else {
                                    growl.error("There was an unknown error processing your request");
                                }
                            }
                            break;
                    }
                    return $q.reject(rejection);
                }
            };
        }]).config(function ($provide, $httpProvider) {
        return $httpProvider.interceptors.push('myHttpInterceptor');
    })

Este é um código aproximado de como esperaria que a chamada de promessa modal se parecesse:

$http.get('/some/url').then(function(c) {
                $uibModalInstance.close(c);
            }, function(resp) {
                if(resp.data.errors) {
                    $scope.errors = resp.data.errors;
                    resp.handled = true;
                    return resp;
                }
            });
    
por Steven M 29.11.2015 в 03:55
fonte

1 resposta

2

1. Solução (caminho hacky)

Você pode fazer isso criando um serviço que faça isso para você. Como as promessas são compatíveis com a cadeia e você basicamente marca uma propriedade handled no nível do controlador, você deve passar essa promessa ao seu serviço e ela cuidará dos erros não manipulados.

myService.check(
    $http.get('url/to/the/endpoint')
             .then( succCallback, errorCallback) 
);

2. Solução (caminho preferido)

Ou a melhor solução seria criar um wrapper para $ http e fazer algo assim:

myhttp.get('url/to/the/endpoint', successCallback, failedCallback);

function successCallback(){ ... }
function failedCallback(resp){
    //optional solution, you can even say resp.handled = true
    myhttp.setAsHandled(resp);

    //do not forget to reject here, otherwise the chained promise will be recognised as a resolved promise.
    $q.reject(resp);
}

Aqui, a chamada de serviço myhttp aplicará os retornos de chamada de sucesso e falha fornecidos e, em seguida, poderá encadear o próprio retorno de chamada do faild e verificar se a propriedade manipulada é verdadeira ou falsa.

A implementação do serviço myhttp (atualizada, adicionada a função setAsHandled , que é apenas opcional, mas é uma solução melhor porque mantém tudo em um só lugar (o atributo 'manipulado' é facilmente modificável e em um único local):

function myhttp($http){
    var service = this;

    service.setAsHandled = setAsHandled;
    service.get = get;

    function setAsHandled(resp){
        resp.handled = true;
    }

    function get(url, successHandler, failedHandler){
        $http.get(url)
             .then(successHandler, failedHandler)
             .then(null, function(resp){
                  if(resp.handled !== true){
                       //your awesome popup message triggers here.
                  }
             })
    }
}

3. Solução

O mesmo que #2 , mas menos código necessário para alcançar o mesmo:

myhttp.get('url/to/the/endpoint', successCallback, failedCallback);

function successCallback(){ ... }
function failedCallback(resp){
    //if you provide a failedCallback, and you still want to have  your popup, then you need  your reject.
    $q.reject(resp);
}

Outro exemplo:

//since you didn't provide failed callback, it'll treat as a non-handled promise, and you'll have your popup.
myhttp.get('url/to/the/endpoint', successCallback);

function successCallback(){ ... }

A implementação do serviço myhttp:

function myhttp($http){
    var service = this;

    service.get = get;

    function get(url, successHandler, failedHandler){
        $http.get(url)
             .then(successHandler, failedHandler)
             .then(null, function(){ 
                 //your awesome popup message triggers here.
             })
    }
}
    
por Iamisti 03.01.2016 / 22:58
fonte