问题描述
我想全局拦截某些$http
错误情况,以防止控制器自行处理错误.我认为我需要HTTP拦截器,但是我不确定如何从控制器中处理错误.
I want to globally intercept certain $http
error scenarios, preventing controllers from handling the errors themselves. I think an HTTP interceptor is what I need, but I'm not sure how to get my controllers from also handling the error.
我有一个像这样的控制器:
I have a controller like this:
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');
});
}
}
还有这样的HTTP拦截器:
And a HTTP interceptor like this:
function HttpInterceptor($q, $location) {
var service = {
responseError: responseError
};
return service;
function responseError(rejection) {
if (rejection.status === 404) {
$location.path('/error');
}
return $q.reject(rejection);
}
}
与浏览器重定向到"/错误"路径一样有效.但是HomeController
中的promise catch也正在执行 ,我不希望那样.
This works, in as much as the browser redirects to the '/error' path. But the promise catch in HomeController
is also executing, and I don't want that.
我知道我可以对HomeController
进行编码,以使其忽略404错误,但这是无法维护的.假设我修改了HttpInterceptor
以便也处理500个错误,那么我就不得不再次修改HomeController
(以及此后可能添加的其他使用$http
的其他控制器).有更优雅的解决方案吗?
I know I could code HomeController
such that it ignores a 404 error, but that's not maintainable. Say I modify HttpInterceptor
to also handle 500 errors, I'd then have to modify HomeController
again (as well as any other controllers that might have since been added that use $http
). Is there a more elegant solution?
推荐答案
选项1-中断/取消承诺链
在HttpInterceptor
中进行小的更改可以破坏/取消承诺链,这意味着将不执行控制器上的activateOk
或activateError
.
Option 1 - Break/cancel the promise chain
A small change in the HttpInterceptor
can serve to break/cancel the promise chain, meaning that neither activateOk
or activateError
on the controller will be executed.
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);
}
}
return $q(function () { return null; })
行取消了诺言.
这是否可以接受是一个争论的话题.凯尔·辛普森(Kyle Simpson)在"不知道JS "状态:
Whether this is "ok" is a topic of debate. Kyle Simpson in "You don't know JS" states:
好吗?坏的?正如我所说,这是一个辩论话题.我喜欢这样的事实,它不需要更改任何现有的$http
使用者.
Good? Bad? As I say, it's a topic of debate. I like the fact that it requires no change to any existing $http
consumers.
凯尔说的很对:
例如,Bluebird Promise库支持取消.来自文档:
The Bluebird promise library for example has support for cancellation. From the documentation:
选项2-不同的抽象
承诺是一个相对广泛的抽象.根据 Promises/A +规范:
Angular $http
服务使用promise的$q
实现为异步HTTP请求的最终结果返回一个promise.
The Angular $http
service uses the $q
implementation of promises to return a promise for the eventual result of an asynchronous HTTP request.
$http
具有两个已弃用的功能,.success
和.error
毫无价值,它装饰返回的诺言.这些功能之所以被弃用,是因为它们不能像promise那样以可链接的方式进行链接,并且被认为不会像"HTTP特定"功能集那样增加太多价值.
It's worth nothing that $http
has two deprecated functions, .success
and .error
, which decorate the returned promise. These functions were deprecated because they weren't chainable in the typical way promises are, and were deemed to not add much value as a "HTTP specific" set of functions.
但这并不是说我们不能创建自己的HTTP抽象/包装程序,甚至不公开$http
所使用的底层承诺.像这样:
But that's not to say we can't make our own HTTP abstraction / wrapper that doesn't even expose the underlying promise used by $http
. Like this:
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;
}
由于这不会返回承诺,因此其消耗也需要有所不同:
Being that this doesn't return a promise, its consumption needs to work a little differently too:
HttpWrapper.get('non-existent-location', getSuccess, getError);
function getSuccess(response) {
alert('Everything is ok');
}
function getError(error) {
alert('An error happened');
}
如果是404,则将位置更改为错误",并且不会执行getSuccess
和getError
回调.
In the case of a 404, the location is changed to 'error', and neither getSuccess
nor getError
callbacks are executed.
此实现意味着链接HTTP请求的功能不再可用.这是可以接受的折衷办法吗?结果可能会有所不同...
This implementation means the ability to chain HTTP requests is no longer available. Is that an acceptable compromise? Results may vary...
感谢TJ发表评论:
HTTP拦截器可以使用属性handled
装饰诺言拒绝,以指示它是否已处理错误.
The HTTP interceptor can decorate the promise rejection with a property handled
to indicate whether it's handled the error.
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);
}
}
控制器看起来像这样:
$http.get('non-existent-location')
.then(function activateOk(response) {
alert('Everything is ok');
})
.catch(function activateError(error) {
if (!error.handled) {
alert('An error happened');
}
});
摘要
与选项2不同,选项3仍然为任何$http
消费者提供链承诺的选项,这在不消除功能的意义上是积极的.
Summary
Unlike option 2, option 3 still leaves the option for any $http
consumer to chain promises, which is a positive in the sense that it's not eliminating functionality.
选项2和3的远距离动作"较少.在选项2的情况下,替代抽象清楚地表明事物的行为将与通常的$q
实现不同.对于选项3,消费者仍会根据自己的喜好做出承诺.
Both options 2 and 3 have less "action at a distance". In the case of option 2, the alternative abstraction makes it clear that things will behave differently than the usual $q
implementation. And for option 3, the consumer will still receive the promise to do with as it pleases.
这3个选项均满足可维护性标准,因为更改全局错误处理程序以处理或多或少的情况不需要更改使用方.
All 3 options satisfy the maintainability criteria, as changes to the global error handler to handle more or less scenarios don't require changes to the consumers.
这篇关于带有全局$ http错误处理的AngularJS的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!