2015-11-29 8 views
9

Ho un gestore di errori globale per la mia app angolare che è scritto come $http interceptor, ma mi piacerebbe fare un ulteriore passo avanti. Quello che mi piacerebbe è che ogni chiamata $http fallisca (viene rifiutata), qualsiasi consumatore "incatenato" della promessa dovrebbe prima tentare di risolvere l'errore, e se è ANCORA irrisolto (non catturato), ALLORA desidero gestore di errori globali da assumere.Global Error handler che rileva solo promesse "non gestite"

Il caso d'uso è, il mio gestore di errori globale mostra un ringhio "alert box" nella parte superiore dello schermo. Ma ho un paio di modali che appaiono e gestisco esplicitamente gli errori lì, mostrando un messaggio di errore nella modale stessa. Quindi, in sostanza, questo controllore modale dovrebbe contrassegnare la promessa respinta come "gestita". Ma dal momento che l'intercettatore sembra sempre essere il primo a correre su un $http error, non riesco a capire un modo per farlo.

Ecco il mio codice intercettore:

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

Questo è il codice di massima di come mi aspetto la chiamata promessa modale per assomigliare:

$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; 
       } 
      }); 
+0

hai pensato a implementarlo sul lato server, invece? Anche quando dici che dovrebbe * cercare di risolvere *, puoi fare un esempio. – Rajesh

+0

Impossibile farlo sul server, l'intero punto è quello di lavorare con il lato client promesse. Cercando di risolvere, voglio dire che il gestore di errori globale dovrebbe essere l'ultimo catchall per errori in una promessa http. Attualmente è la prima cosa a correre su un errore. –

risposta

2

1. Soluzione (modo hacky)

Puoi farlo creando un servizio che lo fa per te. Poiché le promesse sono concatenabili e in pratica contrassegni una proprietà handled a livello di controller, dovresti trasmettere questa promessa al tuo servizio e si prenderà cura degli errori non gestiti.

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

2. Solution (metodo preferito)

O la soluzione migliore sarebbe quella di creare un wrapper per $ http e fare qualcosa di simile:

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

Qui la chiamata di servizio myhttp volontà applica il dato successo e callback non riusciti e quindi può concatenare il proprio callback di faild e verificare se la proprietà gestita è vera o falsa.

L'implementazione del servizio myhttp (aggiornato, aggiunti setAsHandled funzione che è solo facoltativo, ma è una soluzione più bello dal momento che mantiene tutto in un unico luogo (l'attributo 'maneggiato' facilmente modificabili e in un unico luogo):

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. Soluzione

uguale #2 ma meno codice necessario per ottenere lo stesso:

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

O Esempio ther:

//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(){ ... } 

L'implementazione del servizio 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. 
      }) 
    } 
} 
+0

Questo potrebbe funzionare ...Vorrei rimuovere la dipendenza da '' '$ q''' ovunque io stia usando' '' $ http''' anche se ... probabilmente abbastanza facile da fare solo il rifiuto in '' 'setAsHandled'' 'metodo di servizio. –

+0

no, avrai bisogno del tuo $ q, dal momento che devi respingere la tua promessa nel tuo gestore fallito. Altrimenti nella funzione di servizio 'get' non ne prenderà il caso. – Iamisti

+0

giusto, ma sto dicendo che non voglio inject '' '$ q''' in ogni posto che sto usando' '' $ http''' –

Problemi correlati