So I have pulled the interceptor straight from the angular HTTP documentation and yet this still doesn't work. The "request" and "response" functions get called ,but never the "requestError" or the "responseError".
myApp.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push(function ($q) {
return {
'request': function (config) {
return config; //gets called
},
'requestError': function (rejection) {
return $q.reject(rejection); //Never gets called
},
'response': function (response) {
return response; //gets called
},
'responseError': function (rejection) {
return $q.reject(rejection); //Never gets called
}
};
});
}]);
On the server I am returning a 400, but really any error would do. And here is the service
User.publicProfileGetProfile = function (value, type) {
return $http({
url: '/public/profile/' + type + '/' + value,
method: 'GET'
}).then(function (response) {
return response;
}, function(error){
return error;
});
};
No error functions are being called and every response goes through the response function. The standard angular error is displayed with the Bad Request (400) as usual. When the 400 error is returned, it is simply 'undefined' through the 'response' function in the interceptor.
Let me know if I've forgotten to include any important information.
By using return, the error handler is converting the rejection to a success. Instead use throw to chain the rejection.
User.publicProfileGetProfile = function (value, type) {
return $http({
url: '/public/profile/' + type + '/' + value,
method: 'GET'
}).then(function onSuccess(response) {
return response;
}, function onReject(error){
//return converts rejection to success
//return error;
//use throw to chain rejection
throw error;
});
};
When I saw that the JSFiddle (from #georgeawg) was working properly, I made sure mine looked exactly the same. When it didn't work, I looked around to see if I had any other interceptors that might cause problems. I had another interceptor that was being hit first and returning any errors as responses, then they would go through this one and it would process it as a successful response. I removed it and everything seems to be working correct now!
Related
I am trying to get http error if service failed to load a url. I have created a angular factory which is like this:
loadUsers: function () {
return $http.get(urlService.url("/users"));
},
in controller i try to using this factory method to load ruserlist:
urlservice.loadUsers()
.then(function(response) {
$log.info("user loaded");
})
.finally(data.bind(undefined, result));
at this point i want to handle http error but not getting idea where i have to use error function as this is returning a promise. Can someone give me hint.
Just add a .catch to your promise:
urlservice.loadUsers()
.then(function(response) {
$log.info("user loaded");
})
.catch(function(err) {
console.log(err);
})
.finally(data.bind(undefined, result));
add a second callback to the .thenmethod, that will be triggered in case of error.
from the angular doc:
https://docs.angularjs.org/api/ng/service/$http
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
Just add another function inside promise like this
urlservice.loadUsers()
.then(function(response) {
$log.info("user loaded");
},function(response) {
$log.info("error");
})
.finally(data.bind(undefined, result));
urlservice.loadUsers().then(successCallback, errorCallback)
.finally(data.bind(undefined, result));
var successCallback = function(response) {
// handle data recieved
$log.info("user loaded");
};
// create generic method for handling service errors
var errorCallback = function(error) {
// handle error here
$log.info("error occurred");
};
Having an angular service that returns promise, is it possible to detect whether a consumer of this promise handles error ? I'd like to provide a default error handling in service, but ensure that it would be used only if no error handler is defined down the execution chain.
The service method looks like this:
function serviceMethod(method, url, data)
{
return $http({
method: method,
url: url,
data: data
})
.then(
function (response) {
return response;
},
function (response) {
console.log('ERROR!'); // default error handling
}
);
}
The serviceMethod returns a promise, therefore:
1) If the consumer provides error handler, the error should be handled exclusively by it.
$scope.getResponse = function () {
return Services.serviceMethod('put', $scope.url, $scope.someData)
.then(function (response) {
}, function (error) {
// Custom error handling.
});
}
2) If the consumer doesn't provide handler, the error should be handled exclusively by service handler.
Is it possible to achieve in the first successor of serviceMethod? Is it possible at any point in the chain (the error is handled exclusively by the first consumer to provide error handler)?
You have the answer in the code you haven given. Do it like this:
function serviceMethod(method, url, data)
{
return $http({
method: method,
url: url,
data: data
})
.then(
function (response) {
return response;
},
function (response) {
return response; // default error handling
}
);
}
And your getResponse method:
$scope.getResponse = function () {
return Services.serviceMethod('put', $scope.url, $scope.someData)
.then(function (response) {
}, function (error) {
alert(error.code); //Default error handling returned from error function in serviceMethod
alert("My custom error"); //Custom error handling
});
}
It is very important that the rejection handler in the service throw the error response. Otherwise the $q service will convert the rejected promise to a successful response.
function serviceMethod(method, url, data)
{
return $http({
method: method,
url: url,
data: data
})
.then(
function (response) {
return response;
},
function (errorResponse) {
//return response; // default error handling
throw errorResponse;
//OR
//return $q.reject(errorResponse);
}
);
}
A common problem is erroneous conversion of rejected promises to fulfilled promises by failing to return anything. When a function omits a return statement, the function returns a value of undefined. In that case the $q service will convert a rejected promise to a fulfilled promise that resolves with a value of undefined.
That said. No, it is not possible for a service to know how a consumer will use a rejected promise. If a consumer wants a service to skip default error handling, the consumer must specify that in the service call:
function serviceMethod(method, url, data, skipErrorHandling)
{
return $http({
method: method,
url: url,
data: data
})
.then(function (response) {
return response.data;
})
.catch(function (errorResponse) {
if (skipErrorHandling)
throw errorResponse;
}
//Put error handler here
//Correct error
var promise = retry(method, url, data);
return promise;
);
}
I can't seem to wrap my head around when $q/$http should trigger the onReject block.
Let's say I have a basic call:
$http.get('/users')
.then(function(res) {
return res.data;
}, function(err) {
console.log(err);
});
If I get a 500 Internal Server Error I'm going to end up in the onSuccess block. From my meager understanding of promises I guess this seems correct because I technically did get a response? The problem is an onSuccess block seems like the wrong place to have a bunch of
if(res.status >= 400) {
return $q.reject('something went wrong: ' + res.status);
}
Just so that my onReject block will get run. Is this the way it's supposed to work? Do most people handle 400+ statuses in the onSuccess block or do they return a rejected promise to force the onReject block? Am I missing a better way to handle this?
I tried doing this in an httpInterceptor but I couldn't find a way to return a rejected promise from here.
this.responseError = function(res) {
if(res.status >= 400) {
// do something
}
return res;
};
Your success-block will not be hit. Try this and see that error-block will hit if error code > 300.
$http.get('/users').success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
}).error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
You could handle these in an interceptor instead of in your callback.
In your app.config you can configure your $httpProvider to something like this:
$httpProvider.interceptors.push(['$q', function ($q) {
return {
request: function (config) {
//do something
return config;
},
responseError: function (response) {
if (response.status === 401) {
//handle 401
}
return $q.reject(response);
}
};
}]);
I added in my application a "loading screen". As found in this post: 'Click'
Now I have the problem that all $http request results in the "success" callback. Even when the url does not exist.
$http.post("this doesnt even exist", { })
.success(function (data, status, headers, config) {
alert("success"); //this callback is called
})
.error(function (data, status, headers, config) {
alert("error"); //never called
});
When I disable the 'responseInterceptor' everything works fine. (exception, not found, wrong parameters -> all results in error callback)
I'm using a .NET Webservice to get my data from.
The values of parameters in success callback
data: ''
status: 0
headers: 'function(name) { ... }'
config: JSON.stringify(config) '{"method":"POST","url":"this doesnt even exist","data":{}}'
It's because the response interceptor you linked to "swallows" the error.
In the following code:
return promise.then(hide, hide);
The first hide is for the success callback, while the second is for the error callback.
The hide function itself just ends with return r; in all cases, which means it will return the response.
For your $http.post to know there was an error the response interceptor needs to return the promise as rejected instead: return $q.reject(reason);
Something along these lines should hopefully work or at least give further guidance (note that I have not been able to test it):
$httpProvider.responseInterceptors.push(function ($q) {
return function (promise) {
numLoadings++;
loadingScreen.show();
var hide = function () {
if (!(--numLoadings)) loadingScreen.hide();
};
var success = function (response) {
hide();
return response;
};
var error = function (reason) {
hide();
return $q.reject(reason);
};
return promise.then(success, error);
};
});
I need to create chained promises:
var deferred = $q.defer();
$timeout(function() {
deferred.reject({result: 'errror'});
}, 3000);
deferred.promise.then(angular.noop, function errorHandler(result) {
//some actions
return result;
}).then(function successCallback(result) {
console.log('what do I do here?');
return result;
}, function errorCallback(result) {
$scope.result= result;
return result;
});
If I put an errorCallback into the first then, the second then will be resolved and its successCallback will be called . But if I remove errorHandler then second promise will be rejected.
According to Angular JS docs the only way to propagate rejection is to return $q.reject(); and it looks not obvious, especially because I have to inject $q service even if it is not needed;
It can also be done by throwing an exception in errorHandler, but it writes exception trace to console, it is not good.
Is there another option to do this in a clear way? And what is the reason? Why it is done? In which case, the current behavior can be useful?
And what the reason why it is done. In which case, the current behavior can be useful?
It can be useful when in errorHandler you could try to repair error state and resolve promise somehow.
var retriesCount = 0;
function doWork()
{
return $http.post('url')
.then(function(response){
// check success-property of returned data
if(response.data.success)
// just unwrap data from response, may be do some other manipulations
return response.data;
else
// reject with error
return $q.reject('some error occured');
})
.catch(function(reason){
if(retriesCount++ < 3)
// some error, let me try to recover myself once again
return doWork();
else
// mission failed... finally reject
return $q.reject(reason);
});
}
doWork().then(console.log, console.error);
Late to the party, but as I am here;
I prefer to use the $http error for its native error handling, rather than returning a success via a 200 and an error status in the response.
printing 400 or 500 errors in the console is not an issue, if you are debugging you see them if not you don't.
angular.module('workModule', [])
// work provider handles all api calls to get work
.service('workProvider', ['$http', '$q', function($http, $q) {
var endpoint = '/api/v1/work/';
this.Get = function(){
// return the promise, and use 404, 500, etc for errors on the server
return $http.get(endpoint);
};
}])
.controller('workController', ['workProvider', function('workProvider'){
workProvider.Get().then(
function(response){ // success
console.log(response.data);
},
function(response){ // error
console.log(response.data);
}
)
}])