推荐规程,AngularJS应用程序的异常处理 [英] Recommended practice for application exception handling in AngularJS

查看:99
本文介绍了推荐规程,AngularJS应用程序的异常处理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在探讨可能的方法AngularJS来处理应用程序级的异常。

一个我们真的想避免被包裹在嵌套的try / catch块的应用程序的多个部分,但干净办事的事情 - 即响应承诺抛出一个异常


  • 有没有人涉及这个问题前,有什么建​​议?

  • 在如何拿起服务异常以及控制器/指令的任何建议。 (见下面 - 广播工程确定,但只有当你可以附加一个监听到的范围)。

迄今取得的进展

短短的设计目标:


  • 允许从应用程序的一个部分例外情况在其他地方处理 - 或可能多个地方(即显示错误通知用户,禁用插件')

  • 提供的常见错误情况的集中管理 - 即登录到服务器,显示器通知用户,重定向到登录

  • 允许例外,从控制器,指令,服务等被抛出。

  • 最终允许本地化的消息。

我的团队目前倚是写一个服务来处理异常,这将暴露出了一系列简单的呼叫​​:

  exceptionService.warn('exception_token');exceptionService.crit('another_exception_token');

该服务将然后格式化一个例外对象,然后从rootscope播出这一点。这将允许默认处理程序来观看任何节目,并应用默认操作,以及允许定制听众在别人范围进行设置,它可以处理更多的具体情况 - 即禁止用户界面的一部分

  VAR异常= {
    令牌:exception_token',
    严重:暴击
};//广播例外
$ rootScope。$广播(
application_exception',
    例外
);


解决方案

我想差不多最近,它发生,我认为,当涉及到一个很好的错误在JavaScript处理,这是无关紧要的,你所使用的框架,角得也快。我最近写了一个这样的错误处理程序的AngularJS项目,但我做到了在它可以在任何框架中使用的一种方式。

下面是完整的code。您可以直接使用,或修改您的需要...

  / *
工厂errorFact是简化错误处理和其他对象报告。
它支持详细的错误输出文本字符串,在浏览器的控制台。用法示例:支持一个错误的对象返回一个函数将有以下声明
作为其第一行:变种E = errorFact.create(objectName.funcName,参数);
- 在此声明,我们指定完整的对象+方法名作为第一个字符串参数,
- 作为第二个参数,我们通过JavaScript的保留变量称为参数,这些参数
  提供参考所有日志函数的参数。当发生错误时,该函数将返回:返回e.error(错误的说明文字);
  - 这条线将创建并返回一个完整的错误上下文。当支持一个错误的对象返回一个函数进行调用到另一个
该功能还支持错误上下文,那么它可以返回嵌套错误
结果通过将嵌入误差为当前误差对象,而不是错误的
 文本。 例: 变种E = errorFact.create(objectName.funcName,参数);
 VAR数据= callAnotherFunc(...); //调用支持错误对象的功能;
 如果(data.isError){//如果被触发的错误;
    返回e.error(数据); //返回从当前上下文的错误;
 } 顶级code调用一个错误返回函数会做验证
 如果发生错误,记录其所有细节到控制台(典型值)。 例: VAR数据=的getData(...);
 如果(data.isError){
    data.log(); //输出所有错误的详细信息到浏览器的控制台;
 }
 * /使用严格的;app.factory(errorFact功能(){
    返回{
        //创建一个新的错误上下文;
        创建:函数(方法,参数){
            VAR的结果= {
                //启动并返回错误上下文;
                错误:函数(MSG){
                    this.info.isError = TRUE;
                    如果(msg.isError){
                        this.info.details.caller =味精;
                    }其他{
                        this.info.details.msg =味精;
                    }
                    返回this.info;
                },
                信息:
                {
                    ISERROR:假的,
                    详细信息:{},
                    日志:函数(){
                        如果(this.isError){
                            console.error(this.format());
                        }
                    },
                    //格式完全错误的详细信息到一个文本字符串;
                    格式:函数(){
                        如果(this.details.caller){
                            变种的txt = this.details.caller.format();
                            TXT + =\\ nCALLER:+ this.details.method +(+ this.formatArguments()+);
                            返回TXT;
                        }
                        如果(this.details.method){
                            返回错误称之为+ this.details.method +(+ this.formatArguments()+):+ this.details.msg;
                        }其他{
                            返回this.details.msg;
                        }
                        返回;
                    },
                    //格式函数参数的详细信息到一个文本字符串;
                    formatArguments:功能(){
                        如果(!this.details.args){
                            返回;
                        }
                        VAR PARAMS =;
                        对于(VAR I = 0; I< this.details.args.length;我++){
                            如果(params.length大于0){
                                PARAMS + =;
                            }
                            VAR P = this.details.args [I]
                            如果(P ===未定义){
                                PARAMS + =未定义;
                            }其他{
                                如果(P === NULL){
                                    PARAMS + =空;
                                }其他{
                                    如果(typeof运算(P)==对象){
                                        PARAMS + =对象;
                                    }其他{
                                        PARAMS + = P;
                                    }
                                }
                            }
                        }
                        返回PARAMS;
                    }
                }
            };
            如果(方法){
                result.info.details.method =方法;
            }
            如果(参数){
                result.info.details.args = ARGS;
            }
            返回结果;
        }
    }
});

下面是一个工厂,展示如何使用它:

 使用严格的;app.factory('moduleFact',['errorFact',函数(errorFact){
    返回{
        //定位现有模块并扩大其密钥ID引用
        //为相应的对象引用:
        // - 如果'hintGroupId'是present,财产'提示'从加
        //相应的提示组。
        // - 如果'repModules'是present,性质'的问题和裁判
        //添加。
        //如果成功,返回扩展模块对象。
        //如果失败,返回一个错误的对象。
        //
        //注意:目前只支持repModules第一个值。
        expandModule:功能(moduleData,的moduleId){
            变种E = errorFact.create(moduleFact.expandModule,参数);
            如果(!moduleData ||!moduleData.modules ||!的moduleId){
                返回e.error(无效的参数传递);
            }
            VAR MOD = this.findModule(moduleData,的moduleId);
            如果(mod.isError){
                返回e.error(MOD);
            }
            变种SRC = MOD;
            如果(mod.repModules){
                变种REPID = mod.repModules [0];
                如果(!REPID){
                    返回e.error(无效repModules遇到);
                }                ///////////////////////////////////////
                //临时检查抛出一个警告:
                如果(mod.repModules.length→1){
                    console.warn(物业repModules多个值:+ JSON.stringify(mod.repModules)+
                        ,这是不被支持(仅使用第一值));
                }
                ///////////////////////////////////////                SRC = this.findModule(moduleData,REPID);
                如果(src.isError){
                    返回e.error(SRC);
                }
            }
            如果(src.question){
                mod.question = src.question;
            }其他{
                返回e.error(问题不指定);
            }
            如果(src.refs){
                mod.refs = src.refs;
            }
            如果(src.hintGroupId){
                VAR汞柱= this.findHintGroup(moduleData,src.hintGroupId);
                如果(hg.isError){
                    返回e.error(汞);
                }
                mod.hints = hg.hints;
            }
            返回MOD; //需要额外的:扩展属性repModules
        },
        //展开所有模块并返回数据;
        expandAllModules:功能(moduleData){
            变种E = errorFact.create(moduleFact.expandAllModules,参数);
            如果(!moduleData ||!moduleData.modules){
                返回e.error(无效的参数传递);
            }
            对于(VAR I = 0; I< moduleData.modules.length;我++){
                VAR的结果= this.expandModule(moduleData,moduleData.modules [I] .ID);
                如果(result.isError){
                    返回e.error(结果);
                }
            }
            返回moduleData;
        },
        //查找并返回其ID模块;
        findModule:功能(moduleData,的moduleId){
            变种E = errorFact.create(moduleFact.findModule,参数);
            如果(!moduleData ||!moduleData.modules ||!的moduleId){
                返回e.error(无效的参数传递);
            }
            对于(VAR I = 0; I< moduleData.modules.length;我++){
                如果(moduleData.modules [I] .ID ==的moduleId){
                    返回moduleData.modules [I]
                }
            }
            返回e.error(模块使用id =+ +的moduleId找不到);
        },
        //查找并返回提示集团通过其ID;
        findHintGroup:功能(moduleData,hintGroupId){
            变种E = errorFact.create(moduleFact.findHintGroup,参数);
            如果(!moduleData ||!moduleData.hintGroups ||!hintGroupId){
                返回e.error(无效的参数传递);
            }
            对于(VAR I = 0; I< moduleData.hintGroups.length;我++){
                如果(moduleData.hintGroups [I] .ID == hintGroupId){
                    返回moduleData.hintGroups [I]
                }
            }
            返回e.error(提示集团n =+ hintGroupId +找不到);
        }
    }
}]);

所以,当你有这样的工厂的地方,你的高层次code,如控制器只想记录任何问题,如下面的例子:

 使用严格的;app.controller('standardsCtrl',['$范围,moduleFact',函数($范围,moduleFact){        VAR数据= ... //获取数据;
        变种模= moduleFact.expandAllModules(数据);
        如果(mod.isError){
            mod.log(); //记录所有错误的详细信息到控制台;
        }其他{
            //使用数据
        }
    });}]);

I am currently exploring possible methods to handle application-wide exceptions in AngularJS.

One of the things we really wanted to avoid was wrapping multiple parts of the application in nested try/catch blocks, but handle things cleanly - i.e throw an exception in response to a promise.

  • Has anyone covered this issue before and have any recommendations?
  • Any suggestions on how to pick up exceptions in services as well as controllers/directives. (See below - broadcast works ok, but only if you can attach a listener to a scope).

Progress so far

A few short design goals:

  • Allow exceptions from one part of the application to be handled elsewhere - or possibly multiple places (i.e. 'display error notification to user', 'disable widget').
  • Provide central management of common error conditions - i.e. log to server, display notification to user, redirect to login.
  • Allow exceptions to be thrown from controllers, directives, services etc.
  • Eventually allow localized messages.

The current leaning of my team is to write a service to handle exceptions, which would expose a range of simple calls:

exceptionService.warn('exception_token');

exceptionService.crit('another_exception_token');

This service would then format an 'exception' object and broadcast this from the rootscope. This would allow a default handler to watch for any broadcasts and apply default actions, as well as allow custom listeners to be set in others scopes, which could handle more specific conditions - i.e. disable a part of the UI.

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

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

解决方案

I was thinking about the same recently, and it occurred to me that when it comes to a good error handling in javascript, it is irrelevant which framework you are using, Angular on something else. I wrote one such error handler recently for an AngularJS project, but I did it in a way it can be used in any framework.

Here's the complete code. You can either use it directly, or modify to your needs...

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

Below is a factory that shows how it is used:

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

So, when you have such factory in place, your high-level code, such as in a controller would just log any issues as shown in the example below:

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

}]);

这篇关于推荐规程,AngularJS应用程序的异常处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆