33

私は現在、AngularJS でアプリケーション全体の例外を処理するための可能な方法を調査しています。

私たちが本当に避けたかったことの 1 つは、アプリケーションの複数の部分をネストされた try/catch ブロックでラップすることでしたが、物事をきれいに処理しました。つまり、promise に応答して例外をスローしました。

  • 以前にこの問題を取り上げた人がいて、何か推奨事項はありますか?
  • サービスやコントローラー/ディレクティブで例外を検出する方法に関する提案。(以下を参照してください - ブロードキャストは問題なく動作しますが、リスナーをスコープにアタッチできる場合に限ります)。

これまでの進捗

いくつかの短い設計目標:

  • アプリケーションの一部からの例外を別の場所で処理できるようにします。または複数の場所で処理できるようにします (つまり、「ユーザーにエラー通知を表示する」、「ウィジェットを無効にする」など)。
  • 一般的なエラー状態 (サーバーへのログ記録、ユーザーへの通知の表示、ログインへのリダイレクトなど) の集中管理を提供します。
  • コントローラー、ディレクティブ、サービスなどから例外をスローできるようにします。
  • 最終的にローカライズされたメッセージを許可します。

私のチームの現在の傾向は、さまざまな単純な呼び出しを公開する例外を処理するサービスを作成することです。

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 に答える