$http/$q/promise Questions (in Angular) - javascript

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);
}
};
}]);

Related

Can we handle all the errors scenarios of http (4XX-5XX) using catch(error) in JS?

I am using a promise to access some data from some URL and to catch the errors i want to use .catch .
If i miss some error i can anyway catch in .finally but what all errors am i ignoring?
$http.get('/someUrl', config)
.then(function(data) {
}).catch(function activateError(error) {
if (!error.handled) {
alert(error);
}
}).finally(function(){
});
The response of the $http call returns an object with a status property.
It's a number corresponding to the response code of your request.
So you could try handling your errors this way:
$http.get(dataUrl)
.success(function (data){
})
.error(function (error, status){
if (!error.handled) {
alert(error);
}
// handle error treatment with status code
});
Hope it helped!

How can I resolve a request in an AngularJS $httpProvider interceptor?

I hope that I'm missing something here as this seems like it should be pretty straightforward.
I'd like to create an $httpProvider interceptor to take a look at outgoing $http requests and for some (in this case, ones that hit non existent endpoints that I'd like to fake responses for) NOT make the request but resolve it instead with some fake response data.
Here's where I am at right now:
myApp.factory('mockedAPIResponses', ['$q', function($q) {
return {
request: function(config) {
if (/check for endpoint here/.test(config.url)) {
// Resolve request such that it actually makes a request to the server
var defer = $q.defer(config.timeout),
resolvedRequest = defer.resolve({ fakeResponseData : 'foo'});
return resolvedRequest;
}
return config;
}
}
}]);
A few options
1. Handle a response error:
Instead of an object with a ´request` function you should create a factory that returns a response handler in your case, since you are only interested in failed responses, you should implement and return an object with the responseError method:
function factory($q) {
return {
responseError: function(response){
if (response.status === 404) { l
// handle response
return $q.resolve(response)
}
return $q.reject(response);
}
}
}
If you are unable to handle the response you should reject it so other handlers (if any) along the chain have a chance to process it
2. Abort the request and handle the error:
You can abort the request in the request() method and then handle the aborted request in the responseError() method. Add some custom property or code if you want to distinguish it from regular response errors:
function factory($q) {
return {
request: function(config) {
if (/bad/.test(config.url)) {
config.statusText = 'Non existing path';
config.status = 404;
return $q.reject(config);
}
return config;
},
responseError: function(response) {
if (response.status === 404) {
// Handle not found errors
response.status = 200;
response.statusText = 'OK';
response.data = {fake: 'data'}
return $q.resolve(response);
}
return $q.reject(response);
}
}
Here is plunk that first checks the request and if it isn't valid it aborts it. Then it is handled by the responseError() method. You can check the network tab that no actual request is made.
3. Using cache:
Yet another solution (and maybe the easiest) is to redirect the request to a cached route:
function factory($cacheFactory, $q) {
var cache = $cacheFactory('fakePages');
cache.put('cachedUrl', {fake: 'data'});
return {
request: function(config) {
if (/bad/.test(config.url)) {
config.url = 'cachedUrl';
config.cache = cache;
}
return config;
}
}
}
Here is a plunk with the cache implementation

Angular HTTP Interceptor not hitting error functions

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!

$http error callback does not work in combination with httpProvider responseInterceptor

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);
};
});

Angularjs promise rejection chaining

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);
}
)
}])

Categories

Resources