My question is what is the best way to handle errors from http REST calls. Should I use interceptors or decorators? My rest functions look something like this:
queryFunction : function (config) {
var defer = $q.defer();
var config = {};
$http.get(someUrl, config) //or http.put, delete
.then(function (response) {
defer.resolve(response.data);
})
.catch(function (ex) {
defer.reject(ex);
});
return defer.promise;
},
How will the simplest interceptor look?
Here's the code for generic $http ErrorInterceptor:
app.factory('ErrorInterceptor', ['$q', function($q) {
return {
responseError: function(rejection) {
// An error message is shown for all kind of server errors
if (rejection.status == -1) {
//alert('Backend is not working');
//use rejection.data variable
}
return $q.reject(rejection);
}
};
}])
Then it can be included into app config
app.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('ErrorInterceptor');
})
I want to capture all request going out from my one page application like clicking on different tab or any hyperlink.
I have written an interceptor and want to put a ajax call for all request.
.factory('httpRequestInterceptor', function($q,$http){
return {
request: function($http,config){
window.alert(config.url);
var dummyValue = $http.get("url");
return config;
}
}
})
.config(function($httpProvider) {
$httpProvider.interceptors.push('httpRequestInterceptor');
});
I think you are in search of http logger kind of factory. You can use this one
.config(function ($provide, $httpProvider) {
// Intercept http calls.
$provide.factory('MyHttpInterceptor', function ($q) {
return {
// On request success
request: function (config) {
console.log(config); // Contains the data about the request before it is sent.
// Return the config or wrap it in a promise if blank.
return config || $q.when(config);
},
// On request failure
requestError: function (rejection) {
console.log(rejection); // Contains the data about the error on the request.
// Return the promise rejection.
return $q.reject(rejection);
},
// On response success
response: function (response) {
console.log(response); // Contains the data from the response.
// Return the response or promise.
return response || $q.when(response);
},
// On response failture
responseError: function (rejection) {
console.log(rejection); // Contains the data about the error.
// Return the promise rejection.
return $q.reject(rejection);
}
};
});
// Add the interceptor to the $httpProvider.
$httpProvider.interceptors.push('MyHttpInterceptor');
});
I tested, it works for routeProvider navigation as well. For more information please check this blog https://djds4rce.wordpress.com/2013/08/13/understanding-angular-http-interceptors/
$resource calls some API. If it returns some flag, than request should drop to .catch() section of $resource('api/').get(...).$promise.catch();
My interceptor does not trigger that call. It calls .then in any case
Interceptor code:
.factory('SomeHTTPInterceptor', function($q) {
return {
response: function(response) {
if (response.data.someFlag) {
return $q.reject(response);
}
return response;
}
};
})
How to get same response, but in catch section w/o checking for error in every single request?
You have to use the responseError handler as per doc
responseError: interceptor gets called when a previous interceptor threw an error or resolved with a rejection.
.factory('SomeHTTPInterceptor', ['$q', function($q) {
return {
// optional method
'responseError': function(rejection) {
if (rejection.data.someFlag) {
// do something
}
return $q.reject(rejection);
}
};
}]);
I am trying to set up an interceptor to catch 403 responses.
In my config function I have added this to test things out
$httpProvider.interceptors.push(function($q) {
return {
request: function(config) {
console.log('test');
},
responce: function(responce) {
console.log(responce);
if(responce.status === 403) {
console.log('403')
}
return responce || $q.when(responce);
},
responceError: function(rejection) {
console.log(rejection);
if(rejection.status === 403) {
console.log('403', rejection);
}
return $q.reject(rejection);
}
}
});
When I reload the page I get the following output to console:
TypeError: Cannot read property 'headers' of undefined
test
In an infinite loop that crashes the browser tab
If I remove the request method from the object then I never get anything output to console
I am using Angular version: 1.2.22
The first problem is that your request callback is not returning the config parameter, check the $http interceptors documentation:
request: interceptors get called with a http config object. The
function is free to modify the config object or create a new one. The
function needs to return the config object directly, or a promise
containing the config or a new config object.
The second problem is you are spelling response incorrectly, simply change responce and responceError to response and responseError respectively.
DEMO
$httpProvider.interceptors.push(function($q) {
return {
request: function(config) {
console.log('test');
return config;
},
response: function(response) {
console.log(response);
if(response.status === 403) {
console.log('403')
}
return response || $q.when(response);
},
responseError: function(rejection) {
console.log(rejection);
if(rejection.status === 403) {
console.log('403', rejection);
}
return $q.reject(rejection);
}
}
});
Response is spelled with an s, not with a c.
How can I stop a request in Angularjs interceptor.
Is there any way to do that?
I tried using promises and sending reject instead of resolve !
.factory('connectionInterceptor', ['$q', '$timeout',
function($q, $timeout) {
var connectionInterceptor = {
request: function(config) {
var q = $q.defer();
$timeout(function() {
q.reject();
}, 2000)
return q.promise;
// return config;
}
}
return connectionInterceptor;
}
])
.config(function($httpProvider) {
$httpProvider.interceptors.push('connectionInterceptor');
});
I ended up bypassing angular XHR call with the following angular Interceptor:
function HttpSessionExpiredInterceptor(sessionService) {
return {
request: function(config) {
if (sessionService.hasExpired()) {
/* Avoid any other XHR call. Trick angular into thinking it's a GET request.
* This way the caching mechanism can kick in and bypass the XHR call.
* We return an empty response because, at this point, we do not care about the
* behaviour of the app. */
if (_.startsWith(config.url, '/your-app-base-path/')) {
config.method = 'GET';
config.cache = {
get: function() {
return null;
}
};
}
}
return config;
}
};
}
This way, any request, POST, PUT, ... is transformed as a GET so that the caching mechanism can be
used by angular. At this point, you can use your own caching mechanism, in my case, when session
expires, I do not care anymore about what to return.
The $http service has an options
timeout to do the job.
you can do like:
angular.module('myApp')
.factory('httpInterceptor', ['$q', '$location',function ($q, $location) {
var canceller = $q.defer();
return {
'request': function(config) {
// promise that should abort the request when resolved.
config.timeout = canceller.promise;
return config;
},
'response': function(response) {
return response;
},
'responseError': function(rejection) {
if (rejection.status === 401) {
canceller.resolve('Unauthorized');
$location.url('/user/signin');
}
if (rejection.status === 403) {
canceller.resolve('Forbidden');
$location.url('/');
}
return $q.reject(rejection);
}
};
}
])
//Http Intercpetor to check auth failures for xhr requests
.config(['$httpProvider',function($httpProvider) {
$httpProvider.interceptors.push('httpInterceptor');
}]);
Not sure if it is possible in general. But you can start a $http request with a "canceler".
Here is an example from this answer:
var canceler = $q.defer();
$http.get('/someUrl', {timeout: canceler.promise}).success(successCallback);
// later...
canceler.resolve(); // Aborts the $http request if it isn't finished.
So if you have control over the way that you start your request, this might be an option.
I just ended up in returning as an empty object
'request': function request(config) {
if(shouldCancelThisRequest){
return {};
}
return config;
}
Here is what works for me, especially for the purposes of stopping the outgoing request, and mocking the data:
app
.factory("connectionInterceptor", [
"$q",
function ($q) {
return {
request: function (config) {
// you can intercept a url here with (config.url == 'https://etc...') or regex or use other conditions
if ("conditions met") {
config.method = "GET";
// this is simulating a cache object, or alternatively, you can use a real cache object and pre-register key-value pairs,
// you can then remove the if block above and rely on the cache (but your cache key has to be the exact url string with parameters)
config.cache = {
get: function (key) {
// because of how angularjs $http works, especially older versions, you need a wrapping array to get the data
// back properly to your methods (if your result data happens to be an array). Otherwise, if the result data is an object
// you can pass back that object here without any return codes, status, or headers.
return [200, mockDataResults, {}, "OK"];
},
};
}
return config;
},
};
},
])
.config(function ($httpProvider) {
$httpProvider.interceptors.push("connectionInterceptor");
});
If you are trying to mock a result like
[42, 122, 466]
you need to send an array with some http params back, its just how the ng sendReq() function is written unfortunately. (see line 1414 of https://github.com/angular/angular.js/blob/e41f018959934bfbf982ba996cd654b1fce88d43/src/ng/http.js#L1414 or snippet below)
// from AngularJS http.js
// serving from cache
if (isArray(cachedResp)) {
resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3], cachedResp[4]);
} else {
resolvePromise(cachedResp, 200, {}, 'OK', 'complete');
}