exception-handling - AngularJS中应用程序异常处理的推荐做法

标签 exception-handling angularjs

我目前正在探索可能的方法来处理AngularJS中的应用程序范围的异常。

我们真正想要避免的事情之一是将应用程序的多个部分包装在嵌套的try / catch块中,但要处理得很干净-即响应 promise 而抛出异常。

  • 之前有人讨论过此问题吗?
  • 关于如何在服务以及 Controller /指令中获取异常的任何建议。 (请参见下文-广播正常,但前提是您可以将侦听器连接到示波器)。

  • 到目前为止的进展

    一些简短的设计目标:
  • 允许将应用程序某一部分的异常处理到其他地方-或可能在多个地方(即“向用户显示错误通知”,“禁用小部件”)。
  • 提供常见错误情况的集中管理-即登录到服务器,向用户显示通知,重定向到登录。
  • 允许从 Controller ,指令,服务等引发异常。
  • 最终允许本地化消息。

  • 我的团队目前的倾向是编写一个处理异常的服务,这将公开一系列简单的调用:
    exceptionService.warn('exception_token');
    
    exceptionService.crit('another_exception_token');
    

    然后,该服务将格式化“exception”对象并从rootscope广播该对象。这将允许默认处理程序监视任何广播并应用默认操作,并允许在其他范围内设置自定义侦听器,从而可以处理更具体的条件-即禁用UI的一部分。
    var exception = {
        token: 'exception_token',
        severity': 'crit'
    };
    
    // broadcast exception
    $rootScope.$broadcast(
    'application_exception',
        exception
    );
    

    最佳答案

    最近我在考虑相同的问题,我想到当涉及到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");
            }
        }
    }]);
    

    因此,当您拥有这样的工厂时,您的高级代码(例如在 Controller 中)将仅记录任何问题,如下例所示:
        "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
            }
        });
    
    }]);
    

    关于exception-handling - AngularJS中应用程序异常处理的推荐做法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15235731/

    相关文章:

    .net - 在哪里捕获异常

    java - 异常处理反模式 : Why log and return null is an anti-pattern

    spring-mvc - Spring MVC 3.2 异常响应映射

    .net - 如何在 Release模式下记录异常

    javascript - 如何在 ng-repeat 中使用 ng-click?

    javascript - AngularJS 将对象参数绑定(bind)到对象 var

    javascript - 如何在幻灯片部分的页面中隐藏底部零

    language-agnostic - 处理所有异常的正确时机

    javascript - 在 AngularJS 中使用来自不同 Controller 的 $scope 函数

    angularjs - 无法加载 ngTable : 'Module ' ngTable' is not available! '