AngularJS with global $http error handling -
i want globally intercept $http
error scenarios, preventing controllers handling errors themselves. think http interceptor need, i'm not sure how controllers handling error.
i have controller this:
function homecontroller($location, $http) { activate(); function activate() { $http.get('non-existent-location') .then(function activateok(response) { alert('everything ok'); }) .catch(function activateerror(error) { alert('an error happened'); }); } }
and http interceptor 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); } }
this works, in as browser redirects '/error' path. promise catch in homecontroller
also executing, , don't want that.
i know code homecontroller
such ignores 404 error, that's not maintainable. modify httpinterceptor
handle 500 errors, i'd have modify homecontroller
again (as other controllers might have since been added use $http
). there more elegant solution?
option 1 - break/cancel promise chain
a small change in httpinterceptor
can serve break/cancel promise chain, meaning neither activateok
or activateerror
on controller 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); } }
the line return $q(function () { return null; })
, cancels promise.
whether "ok" topic of debate. kyle simpson in "you don't know js" states:
many promise abstraction libraries provide facilities cancel promises, terrible idea! many developers wish promises had natively been designed external cancelation capability, problem let 1 consumer/observer of promise affect other consumer's ability observe same promise. violates future-value's trustability (external immutability), morever embodiment of "action @ distance" anti-pattern...
good? bad? say, it's topic of debate. fact requires no change existing $http
consumers.
kyle's quite right when says:
many promise abstraction libraries provide facilities cancel promises...
the bluebird promise library example has support cancellation. the documentation:
the new cancellation has "don't care" semantics while old cancellation had abort semantics. cancelling promise means handler callbacks not called.
option 2 - different abstraction
promises relatively broad abstraction. promises/a+ specification:
a promise represents eventual result of asynchronous operation.
the angular $http
service uses $q
implementation of promises return promise eventual result of asynchronous http request.
it's worth nothing $http
has two deprecated functions, .success
, .error
, decorate returned promise. these functions deprecated because weren't chainable in typical way promises are, , deemed not add value "http specific" set of functions.
but that's not can't make our own http abstraction / wrapper doesn't expose underlying promise used $http
. 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 doesn't return promise, consumption needs work little differently too:
httpwrapper.get('non-existent-location', getsuccess, geterror); function getsuccess(response) { alert('everything ok'); } function geterror(error) { alert('an error happened'); }
in case of 404, location changed 'error', , neither getsuccess
nor geterror
callbacks executed.
this implementation means ability chain http requests no longer available. acceptable compromise? results may vary...
option 3 - decorate rejection
credit tj comment:
if need error handling in particular controller, need conditions check if error has been handled in interceptor/service etc
the http interceptor can decorate promise rejection property handled
indicate whether it's handled 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); } }
controller looks this:
$http.get('non-existent-location') .then(function activateok(response) { alert('everything ok'); }) .catch(function activateerror(error) { if (!error.handled) { alert('an error happened'); } });
summary
unlike option 2, option 3 still leaves option $http
consumer chain promises, positive in sense it's not eliminating functionality.
both options 2 , 3 have less "action @ distance". in case of option 2, alternative abstraction makes clear things behave differently usual $q
implementation. , option 3, consumer still receive promise pleases.
all 3 options satisfy maintainability criteria, changes global error handler handle more or less scenarios don't require changes consumers.
Comments
Post a Comment