2013-03-05 17 views
32

Obecnie analizuję możliwe metody obsługi wyjątków aplikacji w AngularJS.Zalecana praktyka w zakresie obsługi wyjątków aplikacji w AngularJS

Jedną z rzeczy, których naprawdę chcieliśmy uniknąć, było zawijanie wielu części aplikacji w zagnieżdżonych blokach try/catch, ale radzimy sobie z nimi w sposób czysty - tj. Zgłaszam wyjątek w odpowiedzi na obietnicę.

  • Czy ktokolwiek omówił ten problem wcześniej i czy masz jakieś zalecenia?
  • Wszelkie sugestie dotyczące sposobu odbierania wyjątków w usługach, a także kontrolerów/dyrektyw. (Patrz poniżej - emisja działa dobrze, ale tylko wtedy, gdy możesz dołączyć słuchacza do zakresu).

dotychczasowych postępów

Kilka krótkich cele projektowe:

  • zezwolić na odstępstwo od jednej części wniosku należy obchodzić gdzie indziej - lub ewentualnie wielu miejscach (czyli „powiadomienie o błędzie wyświetlacz do użytkownika "," wyłącz widżet ").
  • Zapewnij centralne zarządzanie typowymi warunkami błędów - tj. Loguj się do serwera, wyświetlaj powiadomienia użytkownikowi, przekieruj do logowania.
  • Zezwalaj na wyjątki wysyłane z kontrolerów, dyrektyw, usług itp.
  • Ostatecznie zezwól na zlokalizowane wiadomości.

Prąd Krzywa mojego zespołu jest napisanie usługi do obsługi wyjątków, które mogłyby narazić szereg prostych połączeń:

exceptionService.warn('exception_token'); 

exceptionService.crit('another_exception_token'); 

Usługa ta będzie następnie sformatować za „wyjątek” obiektu transmisji tego z korzenia. Umożliwi to domyślny moduł obsługi do oglądania wszystkich programów i stosowania domyślnych akcji, a także pozwala ustawić niestandardowe detektory w innych zakresach, które mogą obsługiwać bardziej szczegółowe warunki - tj. Wyłączyć część interfejsu użytkownika.

var exception = { 
    token: 'exception_token', 
    severity': 'crit' 
}; 

// broadcast exception 
$rootScope.$broadcast(
'application_exception', 
    exception 
); 
+2

Czy rzucił okiem na obiekt $ exceptionHandler? Zobacz http://stackoverflow.com/questions/13595469/how-to-override-exceptionhandler-implementation, aby uzyskać więcej informacji. –

+0

Dzięki, przejrzałem. Tak - sprawdziliśmy podany '$ exceptionHandler', który na pozór wydaje się działać dobrze, ale jest w zasadzie powłoką do wdrożenia własnego rozwiązania. Logowanie się do serwera, jak wspomniano w tym poście, jest dobrym pomysłem - jest to jedna z rzeczy, które zdecydowanie chcielibyśmy zrobić. –

+0

Wykonujemy [app] .provider() z użyciem $ exceptionHandler, głównie do rejestrowania produkcji. Nie jest to jednak świetne dla dev, ponieważ ślad stosu nie jest tak dobry. – Sharondio

Odpowiedz

3

Można zastąpić $ exceptionHandler w celu przekazania wyjątki do własnego centrum obsługi wyjątków, ale $ exceptionHandler wydaje się otrzymać tylko wyjątki rzucane od kontrolerów, dyrektyw itp ... ale nie dla wyjątków pochodzących z wywołań ajax. W przypadku tych wyjątków można zaimplementować przechwytywacz podobny do opisanego na tej stronie:

EDYTOWANY: Link nie działa trwale.
Archive.org link

4

Myślałam o to samo niedawno, i przyszło mi do głowy, że jeśli chodzi o dobrej obsługi błędów w javascript, nie ma znaczenia, który ramy używasz, kątowe na coś innego. Ostatnio napisałem jedną z takich procedur obsługi błędów dla projektu AngularJS, ale zrobiłem to w taki sposób, że można go użyć w dowolnej strukturze.

Oto pełny kod. Możesz użyć go bezpośrednio lub zmodyfikować do swoich potrzeb ...

/* 
Factory errorFact is to simplify error handling and reporting in other objects. 
It supports detailed error output as a text string and into the browser's console. 

Usage example: 

A function that supports return of an error object would have the following declaration 
as its very first line: 

var e = errorFact.create("objectName.funcName", arguments); 
- in this declaration we specify the full object + method name as the first string parameter, 
- and as the second parameter we pass javascript's reserved variable called arguments, which 
    provides reference to all of the function's parameters for logging. 

When an error occurs, the function would return: 

return e.error("Error description text"); 
- this line will create and return a complete error context. 

When a function that supports return of an error object makes a call into another 
function that also supports the error context, then it can return the nested error 
result by passing the embedded error to the current error object instead of the error 
text. 

Example: 

var e = errorFact.create("objectName.funcName", arguments); 
var data = callAnotherFunc(...); // calling a function that support an error object; 
if(data.isError){ // If an error was triggered; 
    return e.error(data); // return that error from the current context; 
} 

The top-level code that calls an error-returning function would do verification 
and if an error occurred, log all its details into console (typically). 

Example: 

var data = getData(...); 
if(data.isError){ 
    data.log(); // Output all the error details into the browser's console; 
} 
*/ 

"use strict"; 

app.factory("errorFact", function(){ 
    return { 
     // creates a new error context; 
     create: function(method, args){ 
      var result = { 
       // initiates and returns the error context; 
       error: function(msg){ 
        this.info.isError = true; 
        if(msg.isError){ 
         this.info.details.caller = msg; 
        }else{ 
         this.info.details.msg = msg; 
        } 
        return this.info; 
       }, 
       info: 
       { 
        isError: false, 
        details: {}, 
        log: function(){ 
         if(this.isError){ 
          console.error(this.format()); 
         } 
        }, 
        // formats complete error details into a text string; 
        format: function(){ 
         if(this.details.caller){ 
          var txt = this.details.caller.format(); 
          txt += "\nCALLER: " + this.details.method + "(" + this.formatArguments() + ")"; 
          return txt; 
         } 
         if(this.details.method){ 
          return "Error calling " + this.details.method + "(" + this.formatArguments() + "): " + this.details.msg; 
         }else{ 
          return this.details.msg; 
         } 
         return ""; 
        }, 
        // formats function argument details into a text string; 
        formatArguments: function(){ 
         if(!this.details.args){ 
          return ""; 
         } 
         var params = ""; 
         for(var i = 0;i < this.details.args.length;i ++){ 
          if(params.length > 0){ 
           params += ","; 
          } 
          var p = this.details.args[i]; 
          if(p === undefined){ 
           params += "undefined"; 
          }else{ 
           if(p === null){ 
            params += "null"; 
           }else{ 
            if(typeof(p) == "object"){ 
             params += "Object"; 
            }else{ 
             params += p; 
            } 
           } 
          } 
         } 
         return params; 
        } 
       } 
      }; 
      if(method){ 
       result.info.details.method = method; 
      } 
      if(args){ 
       result.info.details.args = args; 
      } 
      return result; 
     } 
    } 
}); 

Poniżej znajduje się fabryka, która pokazuje, w jaki sposób jest on używany:

"use strict"; 

app.factory('moduleFact', ['errorFact', function(errorFact){ 
    return { 
     // Locates existing module and expands its key Id references 
     // into corresponding object references: 
     // - If 'hintGroupId' is present, property 'hints' is added from 
     // the corresponding hint group. 
     // - If 'repModules' is present, properties 'question' and 'refs' 
     // are added. 
     // On success, return the expanded module object. 
     // On failure, returns an error object. 
     // 
     // NOTE: Currently supports only the first value in repModules. 
     expandModule: function(moduleData, moduleId){ 
      var e = errorFact.create("moduleFact.expandModule", arguments); 
      if(!moduleData || !moduleData.modules || !moduleId){ 
       return e.error("Invalid parameters passed"); 
      } 
      var mod = this.findModule(moduleData, moduleId); 
      if(mod.isError){ 
       return e.error(mod); 
      } 
      var src = mod; 
      if(mod.repModules){ 
       var repId = mod.repModules[0]; 
       if(!repId){ 
        return e.error("Invalid repModules encountered"); 
       } 

       /////////////////////////////////////// 
       // temporary check to throw a warning: 
       if(mod.repModules.length > 1){ 
        console.warn("Multiple values in property repModules: " + JSON.stringify(mod.repModules) + 
         ", which is not supported yet (only the first value is used)"); 
       } 
       /////////////////////////////////////// 

       src = this.findModule(moduleData, repId); 
       if(src.isError){ 
        return e.error(src); 
       } 
      } 
      if(src.question){ 
       mod.question = src.question; 
      }else{ 
       return e.error("Question not specified"); 
      } 
      if(src.refs){ 
       mod.refs = src.refs; 
      } 
      if(src.hintGroupId){ 
       var hg = this.findHintGroup(moduleData, src.hintGroupId); 
       if(hg.isError){ 
        return e.error(hg); 
       } 
       mod.hints = hg.hints; 
      } 
      return mod; // needed extra: expand attribute repModules 
     }, 
     // Expands all the modules and returns the data; 
     expandAllModules: function(moduleData){ 
      var e = errorFact.create("moduleFact.expandAllModules", arguments); 
      if(!moduleData || !moduleData.modules){ 
       return e.error("Invalid parameters passed"); 
      } 
      for(var i = 0;i < moduleData.modules.length;i ++){ 
       var result = this.expandModule(moduleData, moduleData.modules[i].id); 
       if(result.isError){ 
        return e.error(result); 
       } 
      } 
      return moduleData; 
     }, 
     // Locates and returns module by its Id; 
     findModule: function(moduleData, moduleId){ 
      var e = errorFact.create("moduleFact.findModule", arguments); 
      if(!moduleData || !moduleData.modules || !moduleId){ 
       return e.error("Invalid parameters passed"); 
      } 
      for(var i = 0;i < moduleData.modules.length;i ++){ 
       if(moduleData.modules[i].id == moduleId){ 
        return moduleData.modules[i]; 
       } 
      } 
      return e.error("Module with Id = " + moduleId + " not found"); 
     }, 
     // Locates and returns Hint Group by its Id; 
     findHintGroup: function(moduleData, hintGroupId){ 
      var e = errorFact.create("moduleFact.findHintGroup", arguments); 
      if(!moduleData || !moduleData.hintGroups || !hintGroupId){ 
       return e.error("Invalid parameters passed"); 
      } 
      for(var i = 0;i < moduleData.hintGroups.length;i ++){ 
       if(moduleData.hintGroups[i].id == hintGroupId){ 
        return moduleData.hintGroups[i]; 
       } 
      } 
      return e.error("Hint Group with Id = " + hintGroupId + " not found"); 
     } 
    } 
}]); 

Więc, kiedy masz taką fabrykę w lokalu, kod wysokiego poziomu, takich jak w kontrolerze po prostu zalogować dowolny kwestie, jak pokazano w poniższym przykładzie:

"use strict"; 

app.controller('standardsCtrl', ['$scope', 'moduleFact', function($scope, moduleFact){ 

     var data = ...//getting data; 
     var mod = moduleFact.expandAllModules(data); 
     if(mod.isError){ 
      mod.log(); // log all error details into the console; 
     }else{ 
      // use the data 
     } 
    }); 

}]); 
0

co sądzisz stworzyć centralized error handling function dla aplikacji

więc kiedy błąd się stało z twoją frontend rozdarcie (połączenia kątowe, API, ...) jest wykonywany, więc no need to write swój error handlingevery time

więc tutaj jest mój kod

(function() { 
    'use strict'; 
    angular 
     .module('app') 
     .factory('$exceptionHandler', ExceptionHandler); 

    ExceptionHandler.$inject = ['$injector']; //for minification 

    function ExceptionHandler($injector) { 
     var $log, sweetAlert, $translate; 

     return function exceptionHandler(exception, cause) { 
      // Add DI here to prevent circular dependency 
      $log = $log || $injector.get('$log'); 
      sweetAlert = sweetAlert || $injector.get('sweetAlert'); //19degrees.ngSweetAlert2 
      $translate = $translate || $injector.get('$translate'); 
      // $loggerService = $loggerService || $injector.get('$loggerService'); 

      var title, message; 
      title = $translate.instant('General error title'); 
      message = $translate.instant('General error message', { exceptionMessage: exception.message }); 
      sweetAlert.error(title, message); 

      $log.error(exception, cause); 
      // loggerService.logErrorsToBackend(exception, cause); 
     }; 
    } 
})(); 

nie jestem pewien jeśli to podejście jest uważane za najlepszą praktykę, ale mam nadzieję, że ci to pomoże.

Powiązane problemy