处理控制器中的服务错误

时间:2014-07-23 15:09:35

标签: angularjs error-handling angularjs-scope

在我的AngularJS应用程序中,我将从控制器中删除所有$ http.get()调用并将它们放入名为$ myService的自定义服务中,该服务将生成http请求并将promise对象返回给调用服务的控制器。这样做的原因是从我的控制器中删除复制和粘贴代码,所有这些代码都以相同的方式获取http资源,并且所有这些代码都具有错误处理逻辑,这些逻辑也被复制和粘贴。

我遇到的设计问题是我希望错误处理逻辑在一个地方定义并且可以被任何控制器使用。控制器应该能够从服务中请求字符串资源,并将它们分配到范围,如$scope.ButtonText = $myService.resources.ButtonText;,这是理想的,因为它只是一行没有逻辑的代码。问题是,如果错误处理逻辑在服务中,那么我不能(或者至少不应该)从服务内部修改范围。我想要的是如果错误处理逻辑确定出错了那么它应该向控制器返回一个空字符串并将$ scope.error设置为适当的错误消息。

由于我不想在服务中进行范围操作,我首先想到的是我可以让控制器处理错误。例如,如果http请求出现问题,则对$myService.resources.ButtonText的调用可能会返回null,然后控制器可以在每次返回空值时都会出现错误。这个问题是我必须将这个错误处理逻辑复制并粘贴到每个控制器中,撤消我的重构工作!

如何在这种情况下集中错误处理逻辑?

4 个答案:

答案 0 :(得分:5)

所以基本上任何时候都有一个http错误,而不是一个行为,比如转发到错误页面,你希望控制器可以使用特定错误,而不必在每个方法中编写一堆.success().error()样板文件。

您可能尝试的事情是从您的服务中广播错误,然后您可以随时随地收听。拥有一个可以监听的服务或者可能是一个处理在页面上提供错误反馈的控制器可能会很方便。

可能是这个基本想法的变体:

services.factory('myService', ['$q', '$http', '$rootScope', function( $q, $http, $rootScope){
    function getSomething(someID){

        $http.get(url).success(function (data) {
            deferred.resolve(data);
        }).error(function(error) {
            $rootScope.$broadcast('http-error', { foo: 'bar' });
            deferred.reject(null);
        });

然后在某个地方,无论是控制器还是注入控制器的服务,您都可以捕获错误并访问所需的信息:

$rootScope.$on('http-error', function handler(obj) {
    //handle error
    console.log("error:", obj)

 });

如果将此内容写入服务,则可以将错误保存在服务中,而不是注入服务的任何控制器可用的错误,即myErrorService.httpError

答案 1 :(得分:3)

要解决这个问题,我会使用http拦截器和http包装器进行调用。

我实际上正在我的应用中重构这个。

这是拦截器,用于处理http错误并在必要时重定向到登录页面:

app.factory('errorsInterceptor', ['$rootScope', '$q', '$window', '$location', 'localStorageService', 'Logger', function ($rootScope, $q, $window, $location, localStorageService, Logger) {
        return {
            response: function (response) {
                if (response.status === 401) {
                    $location.path('/pages/signin');
                }
                return response || $q.when(response);
            },
            responseError: function (rejection) {
                if (rejection.status === 401) {
                    $window.sessionStorage.redirectUrl = $location.path();
                    $location.path('/pages/signin');
                }
                return $q.reject(rejection);
            }
        };
    }]);

以下是您可以将其插入的方式:

app.config(['$httpProvider', function ($httpProvider) {
    $httpProvider.interceptors.push('errorsInterceptor');
}]);

这是我的http包装器,我将从中进行所有api调用:

(function () {
    'use strict';

    var serviceId = 'HttpService';

    angular.module('app').factory(serviceId, ['$http', '$q', HttpService]);

    function HttpService($http, $q) {

        var service = {
            get: getData,
        };

        return service;

        function getData(url) {
            var deferred = $q.defer();

            $http.get(url).success(function (data) {
                deferred.resolve(data);
            }).error(function (error) {
                // put your global error handling here, in this case it just returns null as you said
                deferred.reject(null);
                // you could also return the error message:
                // deferred.reject(error.message);
            });

            return deferred.promise;
        }
    }
})();

现在举例说明如何在应用程序的控制器中使用它:

(function () {
    'use strict';

    var controllerId = 'ExampleCtrl';

    angular.module('app').controller(controllerId,
        ['$scope', 'HttpService', ExampleCtrl]);

    function ExampleCtrl($scope, HttpService) {

        HttpService.get('my-endpoint-url').then(function(returnedData){
            $scope.title = returnedData;
        });

    }
})();

这样,“title”获取成功时从端点返回的值,或者出现错误时为NULL。 为了更好地理解这一点,我建议研究承诺,这是一个有用的链接: http://liamkaufman.com/blog/2013/09/09/using-angularjs-promises/

希望有所帮助。

答案 2 :(得分:1)

不是从服务返回字符串,而是返回具有值和error属性的对象。如果出现错误,数据将为空,错误将包含控制器可以使用的错误信息。

例如,您的服务可以在错误时返回:

{"value":null, "error":"404 Error"}

或成功:

{"value":"My awesome string", "error":null}

然后在您的控制器中,您可以执行以下操作:

$scope.foo = val.value || val.error

或者更好的东西,根据回报提供不同的行为。

答案 3 :(得分:-1)

你需要写一个http拦截器......

下面是一个例子

module.factory('timestampMarker', [function() {
    var timestampMarker = {
        responseError: function(response) {
            console.log(response);
            toastr.error(response.Message)
            return response;
        }
    };
    return timestampMarker;
}]);
module.config(['$httpProvider', function($httpProvider) {
    $httpProvider.interceptors.push('timestampMarker');
}]);

您可以阅读此great article on the http interceptors

关于http interceptor

的另一篇文章

编辑(根据评论)

要求是具有全局错误处理机制,该机制在应用程序中的任何http调用中的任何类型的失败时都返回null ...

httpInterceptor是你可以对http调用进行全局处理的地方...... 例如如果您从服务器获得401 /未经身份验证的响应,则可以将用户重定向到登录页面,或者 如果出现错误,您可以在控制台中记录该错误或显示警告

所以这个错误处理是针对所有的http调用实现的,它会让你保持个人的http类DRY。

你可以使用这个httpInterceptor为http调用响应中的任何类型的错误返回null,所以如你所说,你不必在每个http响应中写set to null语句。

但是,我的建议是反对在所有类型的错误上返回null,并让单独的http调用决定如何处理错误。是的,实现全局错误处理,但这应该仅用于401错误等错误。