AngularJS具有全局$ http错误处理

Sni*_*tor 15 angularjs

我想全局拦截某些$http错误情况,阻止控制器自己处理错误.我认为HTTP拦截器是我需要的,但我不知道如何让我的控制器也处理错误.

我有一个像这样的控制器:

function HomeController($location, $http) {
    activate();

    function activate() {
        $http.get('non-existent-location')
            .then(function activateOk(response) {
                alert('Everything is ok');
            })
            .catch(function activateError(error) {
                alert('An error happened');
            });
    }
}
Run Code Online (Sandbox Code Playgroud)

像这样的HTTP拦截器:

function HttpInterceptor($q, $location) {
    var service = {
        responseError: responseError
    };

    return service;

    function responseError(rejection) {
        if (rejection.status === 404) {
            $location.path('/error');
        }
        return $q.reject(rejection);
    }
}
Run Code Online (Sandbox Code Playgroud)

这与浏览器重定向到'/ error'路径一样有效.但在承诺抓HomeController执行了,我不希望这样.

我知道我可以编码HomeController,它忽略了404错误,但这是不可维护的.假设我修改HttpInterceptor为也处理500个错误,然后我必须HomeController再次修改(以及可能已经添加的任何其他控制器使用$http).有更优雅的解决方案吗?

Sni*_*tor 22

选项1 - 中断/取消承诺链

在一个小的变化HttpInterceptor可以用来打破/取消承诺链,这意味着既不activateOkactivateError在控制器上将被执行.

function HttpInterceptor($q, $location) {
    var service = {
        responseError: responseError
    };

    return service;

    function responseError(rejection) {
        if (rejection.status === 404) {
            $location.path('/error');
            return $q(function () { return null; })
        }
        return $q.reject(rejection);
    }
}
Run Code Online (Sandbox Code Playgroud)

这条线return $q(function () { return null; }),取消了承诺.

这是否"好"是一个争论的话题.Kyle Simpson在" 你不懂JS "中指出:

许多Promise抽象库提供了取消Promises的功能,但这是一个糟糕的主意!许多开发人员希望Promises本身具有外部取消功能,但问题在于它会让一个Promise的消费者/观察者影响其他消费者观察同一个Promise的能力.这违反了未来价值的可信度(外部不变性),但更多的是"远距离行动"反模式的体现......

好?坏?正如我所说,这是一个辩论的话题.我喜欢这样一个事实,它不需要改变任何现有的$http消费者.

凯尔说得很对,他说:

许多Promise抽象库提供了取消Promises的工具......

例如,Bluebird promise库支持取消.从文档:

新取消具有"不关心"语义,而旧取消具有中止语义.取消promise仅仅意味着不会调用其处理程序回调.

选项2 - 不同的抽象

承诺是一个相对宽泛的抽象.来自Promises/A +规范:

表示异步操作的最终结果.

Angular $http服务使用$qpromises 的实现来返回异步HTTP请求的最终结果的承诺.

没有任何东西$http两个已弃用的函数,.success并且.error它们装饰了返回的promise.这些函数已被弃用,因为它们不是以承诺的典型方式链接的,并且被认为不会作为"HTTP特定"函数集添加很多价值.

但这并不是说我们不能制作我们自己的HTTP抽象/包装器,它甚至不暴露使用的潜在承诺$http.像这样:

function HttpWrapper($http, $location) {
    var service = {
        get: function (getUrl, successCallback, errorCallback) {
            $http.get(getUrl).then(function (response) {
                successCallback(response);
            }, function (rejection) {
                if (rejection.status === 404) {
                    $location.path('/error');
                } else {
                    errorCallback(rejection);
                }
            });
        }
    };

    return service;
}
Run Code Online (Sandbox Code Playgroud)

由于这不会回报承诺,它的消费也需要有所不同:

HttpWrapper.get('non-existent-location', getSuccess, getError);

function getSuccess(response) {
    alert('Everything is ok');
}

function getError(error) {
    alert('An error happened');
}
Run Code Online (Sandbox Code Playgroud)

在404的情况下,位置变为"错误",并且getSuccess也不getError执行回调.

此实现意味着链接HTTP请求的功能不再可用.这是可以接受的妥协吗?结果可能不同......

选项3 - 装饰拒绝

感谢TJ的评论:

如果您需要在特定控制器中进行错误处理,则需要条件来检查拦截器/服务等中是否已处理错误

HTTP拦截器可以使用属性handled来修饰promise拒绝,以指示它是否处理了错误.

function HttpInterceptor($q, $location) {
    var service = {
        responseError: responseError
    };

    return service;

    function responseError(rejection) {
        if (rejection.status === 404) {
            $location.path('/error');
            rejection.handled = true;
        }

        return $q.reject(rejection);
    }
}
Run Code Online (Sandbox Code Playgroud)

控制器然后看起来像这样:

$http.get('non-existent-location')
    .then(function activateOk(response) {
        alert('Everything is ok');
    })
    .catch(function activateError(error) {
        if (!error.handled) {
            alert('An error happened');
        }
    });
Run Code Online (Sandbox Code Playgroud)

摘要

与选项2不同,选项3仍然为任何$http消费者提供链接承诺的选项,这在某种意义上是积极的,因为它没有消除功能.

选项2和3都具有较少的"远距离动作".在选项2的情况下,替代抽象清楚地表明事物的行为与通常的$q实现不同.对于选项3,消费者仍然会收到随心所欲的承诺.

所有3个选项都满足可维护性标准,因为更改全局错误处理程序以处理更多或更少的方案不需要更改使用者.