2013-03-05 76 views
32

應用程序異常處理建議的做法我目前正在研究可行方法AngularJS來處理應用程序範圍內的異常。在AngularJS

一個我們真的想避免被包裹在嵌套的try/catch塊的應用程序的多個部分,但乾淨辦事的事情 - 即響應承諾拋出異常。

  • 有沒有人有過這個問題,並有任何建議?
  • 如何拿起服務異常以及控制器/指令的任何建議。 (見下文 - 廣播作品好,但只有當你可以附加一個聽衆的範圍)。

迄今取得的進展

短短的設計目標:

  • 允許從應用程序的一個組成部分例外情況在其他地方處理 - 或可能多個地方(即「顯示錯誤通知到用戶','禁用小部件')。
  • 提供的共同的錯誤條件中央管理 - 即登錄到服務器,顯示通知用戶,重定向到登錄。
  • 允許例外,從控制器,指令,服務等
  • 拋出最終允許本地化的消息。

我的團隊目前斜塔是寫一個服務來處理異常,這將暴露出了一系列簡單的呼叫:

exceptionService.warn('exception_token'); 

exceptionService.crit('another_exception_token'); 

該服務將然後格式化的「例外」對象和廣播這從根目錄。這將允許默認處理程序監視任何廣播並應用默認操作,並允許在其他範圍中設置自定義偵聽器,這可以處理更具體的條件 - 即禁用部分UI。

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

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

你看看$ exceptionHandler對象嗎?有關更多信息,請參閱http://stackoverflow.com/questions/13595469/how-to-override-exceptionhandler-implementation。 – 2013-03-06 23:11:55

+0

謝謝,我已經看過了。是的 - 我們已經查看了提供的'$ exceptionHandler',表面上看起來運行良好,但基本上是一個用於實現自己的解決方案的shell。如該文章中提到的那樣記錄到服務器是一個好主意 - 這是我們肯定想要做的事情之一。 – 2013-03-07 03:54:44

+0

我們正在做一個[app] .provider(),其中包含$ exceptionHandler,主要用於生產日誌記錄。儘管如此,開發並不是那麼好,因爲堆棧跟蹤不太好。 – Sharondio 2013-03-08 18:23:05

回答

3

您可以覆蓋$ exceptionHandler的,以便將異常傳遞到例外自己的核心業務,但$ exceptionHandler的似乎只能接收來自您的控制器,指令等引發的異常...但不對於源自ajax調用的異常。對於這些例外可以實現像此頁上描述了一個攔截器:

編輯:鏈接已經死了永久。
Archive.org link

4

我想差不多最近,它發生,我認爲,當涉及到一個好的錯誤在JavaScript處理,這是無關緊要的,你所使用的框架,角得也快。我最近寫了一個這樣的錯誤處理程序的AngularJS項目,但我做到了,它可以在任何架構中使用的方法。

下面是完整的代碼。您可以直接使用它,或根據需要修改...

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

下面是一個工廠,展示如何使用它:

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

所以,當你有這樣的工廠的地方,你的高層次的代碼,如在控制器將只需登錄任何問題如下圖所示的例子:

"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

什麼是您的意見,您的應用

創建一個

所以任何時候你的前端撕裂發生錯誤(角,API調用,...),它執行的,所以你no need to writeerror handlingevery time

所以這裏是我的代碼

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

我不知道如果這種方法被認爲是最佳實踐,但希望它能幫助你。