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

Popular posts from this blog

PySide and Qt Properties: Connecting signals from Python to QML -

c# - DevExpress.Wpf.Grid.InfiniteGridSizeException was unhandled -

scala - 'wrong top statement declaration' when using slick in IntelliJ -