33

我目前正在探索在 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
);
4

3 回答 3

4

我最近也在考虑同样的问题,我突然想到,当涉及到 javascript 中的良好错误处理时,您使用的框架无关紧要,而 Angular 在其他东西上。我最近为一个 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
        }
    });

}]);
于 2013-10-22T19:18:20.480 回答
3

您可以重写 $exceptionHandler 以便将异常传递给您自己的中央服务以获取异常,但 $exceptionHandler 似乎只接收从您的控制器、指令等抛出的异常......但不适用于源自 ajax 调用的异常. 对于这些异常,您可以实现一个拦截器,如本页所述:

已编辑:链接永久失效。
Archive.org 链接

于 2013-03-18T20:36:43.297 回答
1

centralized error handling function为您的应用程序创建一个您的意见是什么

所以每当你的前端撕裂(角度,API调用,......)发生错误时,它就会执行,所以no need to write你的error handling every 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);
        };
    }
})();

我不确定这种方法是否被认为是最佳实践,但希望它对您有所帮助。

于 2017-06-26T08:58:38.320 回答