On our site, I get into this predicament where you basically transfer a project over to another user. When that happens, if the original user tries to view the project he/she just transferred, we give a 403 back because they no longer are the owner of the project. I started to look up interceptors in angular. I hooked up the responseError to just see if it gets called on the 403 like so
.config(($httpProvider) => {
$httpProvider.interceptors.push(function() {
return {
responseError: function(rejection) {
console.log("bad response");
return rejection;
}
}
});
});
So my "bad response" gets called and everything, but I was not sure how I could show a modal view or something at this point that shows the error to the user since this 403 response actually happens on a few of our different resources, not just projects. Any thoughts? Thanks in advance.
If i understand correctly you want to show the error dialog only for some http calls not every call that goes through you interceptor. You could probably try this:-
Set a config for your http calls say handleError:true.
$http.get('myurl', {handleError:true})....
$http.post('myurl',data, {handleError:true})....
etc..
and in your interceptor look for that specific config setting to display the error:-
$httpProvider.interceptors.push(function() {
return {
responseError: function(rejection) {
console.log("bad response");
if(rejection.config.handleError && rejection.status === 403){
//show error dialog
}
return rejection;
}
}
});
Also you could possibly send the status code that needs to be handled as well.
$http.get('myurl', {handleStatus:[403,...]})....
and
$httpProvider.interceptors.push(function() {
return {
responseError: function(rejection) {
if((rejection.config.handleStatus || []).indexOf(rejection.status) > -1){
//show error dialog. probably you could show it in a $timeout to make this async.
}
return rejection;
}
}
});
Related
I have an AngularJS application with an interceptor to display error messages.
Sometimes the error message from the backend needs some front-end treatment to give more context, like changing a "access denied" into "You can't do this because of X".
How can I do this so the interceptor does not get called?
Right now I am endind up with 2 messages. The message from my controller and the message from the interceptor.
Solution:
service.js:
myFunction: function(id) {
return $http.post('myUrl/', {}, {skipErrorInterceptor: true});
}
interceptor.js:
'responseError': function(rejection) {
if (rejection.config.skipErrorInterceptor) {
return $q.reject(rejection);
} else {
... Global message treatment
}
return $q.reject(rejection);
}
Running my Angular app without a server running should return an error, or course. But this "ERR_CONNECTION_REFUSED" I see in my Chrome's console triggers my success function of my membership service:
function login(user, onSuccess, onError){
$http.post(_loginAddress, user)
.success(loginSuccess)
.success(onSuccess)
.error(loginError)
.error(onError);
}
function loginSuccess(data) {
// this code executes with data = null here.
if (data.ok === true) {
...
}
}
function loginError(data, code) {
...
}
The relevant section of the $http docs states:
A response status code between 200 and 299 is considered a success status and will result in the success callback being called. Note that if the response is a redirect, XMLHttpRequest will transparently follow it, meaning that the error callback will not be called for such responses.
Am I suppose to program my $http success() functions for possible false-positive?
EDIT
So .. I went hunting and found that one of my interceptors (authentication) was causing this ERR_CONNECTION_REFUSED error response to be considered as 'recovered' and therefore the success function was called.
I changed this:
if (response.status !== 401) {
return response;
}
To this:
if (response.status !== 401) {
return $q.reject(response);
}
And everything is fine again.
Hope this might help someone else.
I am in process of creating Offline mode in an app. Whenever a http request is send, I would want an interceptor to detect the network state. If the state is no connectivity, I would want to create a mock response and make it feel like as if the response is coming from a server.
You could check if you are online or not by reading the status of the response in your interceptor, if its a 401 / 501/ etc:
var interceptor = ['$rootScope', '$q', function ($rootScope, $q) {
function success(response) {
return response;
}
function error(response) {
var status = response.status; // error code
if ((status >= 400) && (status < 500)) {
$rootScope.broadcast("AuthError", status);
return;
}
if ((status >= 500) && (status < 600)) {
$rootScope.broadcast("ServerError", status);
return;
}
// otherwise
return $q.reject(response);
}
return function (promise) {
return promise.then(success, error);
}
}]
There's another way, using html5, but I guess will not work on some browsers. This is done using navigator.onLine property, like:
if (navigator.onLine) {
//I'm online
} else {
I'm not online
}
(https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine/onLine)
If you throw an object inside your request interceptor this will trigger a call to the responseError interceptor, passing the throwed object as its argument.
You have to find a way notify responseError interceptor that this is not a real error and that it should return a custom response.
The responseError interceptor can choose to recover error returning a response object o fail returning a rejected promise (check angular's $http interceptor docs)
EDIT: If you throw an object from request interceptor you can't avoid an error message in console. It's better to return a rejected promise passing config object as value and put extra information in it for responseError detect the special situation.
Every time I hit the servers using any $resource I want to show the same alert to my users whenever it fails.
Today, it looks like:
function tryAgain() { alert("try again") }
myResource.query().$promise.catch(tryAgain);
myResource.update(...).$promise.catch(tryAgain);
myResource.delete(...).$promise.catch(tryAgain);
otherResource.query().$promise.catch(tryAgain);
Is there a way to configure the default error handling function for ngResource? I'm looking for something like:
$resource.somethingMagicHere.defaults.catch(tryAgain);
You can use an interceptor in your app.config() section. This will catch all response errors originating from $http which $resource uses.
$httpProvider.interceptors.push(function($q) {
return {
'responseError': function(response) {
if (response.status == 401) {
// Handle 401 error code
}
if (response.status == 500) {
// Handle 500 error code
}
// Always reject (or resolve) the deferred you're given
return $q.reject(response);
}
};
});
The #kba answer helped me find the path. The following article made me understand it:
http://www.webdeveasy.com/interceptors-in-angularjs-and-useful-examples/
Just declare it once and reuse:
var defaultErrorHandler = function() {
alert("try again")
}
myResource.query(...).$promise.catch(defaultErrorHandler());
myResource.update(...).$promise.catch(defaultErrorHandler());
...
I have created http interceptors and attached to my main app as below:
angular.module('app',['ui.router'])
.factory('AuthInterceptor',function($window,$q){
return{
request: function(config){
console.log("config object is");
console.log(config);
if($window.sessionStorage.getItem('token')){
config.headers['x-access-token']= $window.sessionStorage.getItem('token');
}
return config || $q.when(config);
},
response: function(response){
console.log("response object is:");
console.log(response);
if (response['status'] >= 400) {
console.log("Not Authorized.kindly login first");
$state.transitionTo('login');
}
return response || $q.when(response);
}
};
})
.config(function($httpProvider){
$httpProvider.interceptors.push('AuthInterceptor');
});
On the server-side (some express code) I am checking if the user is authorized or not and if not I respond with the following code (only displaying a subset of code to keep things concise):
if(!req.user){
res.send('Not authorized',400);
}
The server code works fine (i can see it in the network tab of the chrome developer tools)
However AuthInterceptor.response() does nto get called.
However do note that the AuthInterceptor.response() does get executed when the response status is 200
So I am confused.. why is it not intercepting 400 statuses ?
If the response status code is outside of the range [200-299], then the responseError interceptor is called.
You need to provide a responseError interceptor:
return {
request: function (config) {...},
responseError: function (rejection) {...}
};