Error status codes are not being caught - angularjs

I am trying to catch angular resource's HTTP error status code (!=200).
My Service, where I have resources defined:
(apiService.js)
.factory('ApiService', function($resource, $http, localStorageService, CONFIG) {
var base_api_url = api_url = CONFIG.api_url, api_version_prefix = CONFIG.api_version_prefix;
return {
userDevices: $resource(api_url+'/requestRegistration/userDevices/:action', {}, {
registerDevice: {
method: 'POST',
params: {
action: ''
}
},
verify: {
method: 'POST',
params: {
action: 'verify'
}
},
}
}
});
My controller's code:
.controller('LoginCtrl', function(CONFIG, $scope, $state, $ionicPlatform, $ionicPopup, ApiService) {
$scope.data = {
username: null
};
$scope.registerDevice = function() {
if($scope.data.username) {
var authenticationResponse = ApiService.userDevices.registerDevice({
username: $scope.data.username
});
authenticationResponse.$promise.then(function(result) {
// this is always fired, even then response code is 400 or 503 :( I am not able to check response status code.
console.log(result);
console.log('success!');
}, function(error){
// this code is not being exectued even when response status code is different then 200
// its never executed at all :(
console.log('error!');
});
}
};
});
When I send the request and I receive response code 400/503, I believe that function(error) code should be executed but it's not.
Instead, my code in $promise.then(function(result)(...) is executed and I am not able to detect a response HTTP status code.
So, my questions:
Why isn't my error handling function being executed?
How can I detect HTTP response status codes?

The first .catch is converting rejections to fulfilled. To prevent conversion, the .catch method needs to throw the error.
authenticationResponse.$promise.catch(function(error){
alert('catched error!!!');
//throw to chain error
throw error;
}).then(function(result) {
// this is always fired, even then response code is 400 or 503 :(
console.log(result);
console.log('success!');
//return to chain data
return result
}, function(error){
// This should be executed when status code is different then 200?
// its never executed at all :(
console.log('error!');
//throw to chain rejection
throw error;
});
When a function omits a return or throw statement, it returns undefined. The $q service creates a derived promise that resolves to undefined.
Diagnosing ngResource Problems
To diagnose problems with $resource methods, add a response interceptor:
userDevices: $resource(api_url+'/requestRegistration/userDevices/:action', {}, {
registerDevice: {
method: 'POST',
params: {
action: ''
},
interceptor: {
response: function (response) {
console.log("registerDevice success");
console.log(response.status);
return response;
},
errorResponse: function (errorResponse) {
console.log("registerDevice error");
console.log(errorResponse.status);
throw errorResponse;
}
}
},
verify: {
method: 'POST',
The other thing to look for is other $http interceptors in the App converting responses by omitting a throw statement.

There is a function to capture the response of the HTTP status in Angular. You can look to see how it is done here stack overflow http response

You can use an interceptor.
Intercept requests before they are handed to the server and responses
before they are handed over to the application code that initiated
these requests
So,
This will catch all response errors originating from $http which $resource.
$httpProvider.interceptors.push(function($q) {
return {
'responseError': function(response) {
if (response.status == 400) {
// Handle 400 error code
}
if (response.status == 503) {
// Handle 503 error code
}
// Rejects the derived promise.
return $q.reject(response);
}
};
});

Related

Using $promise.then() after Interceptor erroneously calls successCallback

i want to use $promise.then(successCallback,errorCallback) after the interceptor did the basic message handling. Somehow it always runs into the successCallback and never into the errorCallback if i get error 400,401 etc. If i remove the interceptor it works fine. I also found out that i could create a new promise in the interceptors to solve this problem but i think this is not the best way to go. In my case i only want an interceptor for some basic message handling and then continue in the main controller with the main logic. This is currently not possible because i always run into the successCallback if i get an error.
Did i understand something wrong?
Here an example what i want:
var resourceObj = $resource("url/abc/:action/:id", {}, {
getMember: {
method: 'GET',
params: { action: 'member' },
interceptor: {
responseError: function(response) {
console.log("Interceptor responseError");
//Show some default error messages here
return response; //create new promise with $http(response); to solve the problem?
},
response: function(response) {
console.log("Interceptor responseSuccess");
//Show some default success messages here
return response; //create new promise with $http(response); to solve the problem?
}
}
}
});
myModule.controller('myCtrl', function($scope) {
$scope.checkMember() = function(memberId) {
resourceObj.getMember({ id: memberId }, {}).$promise.then(
function(responseOK) { //successCallback
console.log(responseOK);
$scope.testMember = responseOK.data; // or if no interceptor is used responseOK.toJSON()
//do some stuff here after async call is finished
},
function(responseError) { //errorCallback, never called in error case if an interceptor is used.
console.log(responseError);
//do maybe some advanced error handling here after async call is finished
}
);
}
});
It is important to re-throw the error response.
var resourceObj = $resource("url/abc/:action/:id", {}, {
getMember: {
method: 'GET',
params: { action: 'member' },
interceptor: {
responseError: function(response) {
console.log("Interceptor responseError");
//Show some default error messages here
̶r̶e̶t̶u̶r̶n̶ ̶r̶e̶s̶p̶o̶n̶s̶e̶;̶
//IMPORTANT re-throw error response
throw response;
},
response: function(response) {
console.log("Interceptor responseSuccess");
//Show some default success messages here
return response;
}
}
}
});
If the error response is simply returned, it will be erroneously converted from a rejection to a success.

AngularJS $HTTP error status of -1

I currently have an error intercept set up in AngularJS to catch any failed HTTP requests so that I can notify myself when it occurs. Occasionally (but often enough) I get an error with a status of -1. I have no idea what this means or why its occurring... Anyone have any explanations?
appService.factory("$errorInterceptor", ['$q', '$injector', function ($q,$injector) {
var errorInterceptor = {
responseError: function (response) {
var $http = $injector.get('$http');
if (response.status !== 401 && response.status !== 409) {
var error = {
status: response.status,
message: JSON.stringify(response),
url: window.location.href,
};
$http.post("/api/errors/angular", error);
}
return $q.reject(response);
}
}
return errorInterceptor;
}])
and the repsponse I'm getting is..
{"data":null,"status":-1,"config":{"method":"PUT","transformRequest":[null],"transformResponse":[null],"url":"/api/baskets/changedeliverymethod/10","headers":{"Accept":"application/json, text/plain, */*"}},"statusText":""}
EDIT:
I have never actually been able to produce this myself. Just get it randomly from all types of devices/browsers.

How do I test an $httpBackend POST error state with AngularJS / Karma?

I'm trying to write a unit test for my Angular Service and here's a function in the service:
login = function(authObject) {
deferred = $q.defer();
$http({
url: '/api/v1/session/create',
method: 'POST',
data: authObject
}).success(function(response) {
var user;
if (response.status === 'ok' && response.user && response.authenticated === true) {
user = response.user;
}
return deferred.resolve(response);
}).error(function(data) {
deferred.reject(data);
return $state.go('api_error');
});
return deferred.promise;
};
I can successfully test the success case with something like:
it('should go to the api error state', function() {
var authObject;
authObject = {
username: 'a#b.com',
password: 'c'
};
$httpBackend.expectPOST('/api/v1/session/create').respond(someData);
userService.login(authObject).then(function(response) {
return console.log("not error", response);
}, function(response) {
return console.log("error", response);
});
return expect($state.go).toHaveBeenCalledWith('api_error');
});
That works fine, however if I do:
$httpBackend.expectPOST('/api/v1/session/create').respond(500, 'error');, then the error case doesn't get called. What am I doing wrong?
In order for your .then() error callback to be called, the previous promise in the chain should result in error (e.g. throw an Exception) or be rejected.
Returning 500 will cause the error callback in your login() method to be called, but since that callback neither throws an Error nor gets rejected, your chained error callback won't be called.
E.g. changing:
}).error(function(data) {
deferred.reject(data);
return $state.go('api_error');
});
to:
}).error(function(data) {
return deferred.reject(data);
//return $state.go('api_error');
});
would work (but it doesn't do what you want :D).
I am not familiar with ui-router, but in this case it could be possible that $state.go() aborts the current execution chain, so I am not sure the following would work:
}).error(function(data) {
$state.go('api_error');
return deferred.reject(data);
});

Angular check each resource response for error flag

In Angular, I would like to have a function that intercepts the $resource response before it gets to the controller to see if it has an error flag set and then act on that error flag. Is there a resource function I can hook into to check the response data before it sends it on it's way to the controller?
Sample resource :
mymod.factory('setSomething', function($resource){
var resource = $resource('/proxy/request.php?action=setSomething', {}, {
post:{
method : "POST",
isArray : false,
headers : {
'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'
}
},
});
return resource;
});
You can define error handling for resources globally:
$httpProvider.interceptors.push(function($q, $rootScope) {
return {
'request': function(config) {
// intercepts the request
},
'response': function(response) {
// intercepts the response. you can examine things like status codes
},
'responseError': function(response) {
// intercepts the response when the response was an error
}
}
});
The response parameter gives you a lot of information to work with so you can handle errors appropriately.

How do I get the HTTP response status code in AngularJS 1.2

Using ngResource in AngularJS 1.2rc(x), how do I get the status code now?
RestAPI.save({resource}, {data}, function( response, responseHeaders ) {
});
where RestAPI is my ngResource.
The response has the $promise object and the resource returned from the server but not a status anymore. The responseHeaders() function only has a status if the server injects the status code into the header object, but not the true returned status code. So some servers may serve it and some might not.
You can use the promiss callbacks then, catch and finally after the $resource call.
For example. If you want to catch an error after a call, you would do something like this:
RestAPI.save({resource}, {data}, callbackFunction).$promise.catch(function(response) {
//this will be fired upon error
if(response.status == 500) alert('Something baaad happend');
}).then(function() {
//this will be fired upon success
});
The response object will have status and the statusText properties. status being an integer status code and statusText the text. You'll also have the data property containing the server response.
edit: as suggested, it was response.status
You must add an interceptor inside your resource declaration. Like this:
var resource = $resource(url, {}, {
get: {
method: 'GET'
interceptor: {
response: function(response) {
var result = response.resource;
result.$status = response.status;
return result;
}
}
}
});
Usage:
resource.get(params, function(result) {
console.log(result.$status)
});
IMO status code should have been provided by default.
There is an issue for this https://github.com/angular/angular.js/issues/8341
For anyone using a newer version of Angular, looks like we've had access to the status code as a 3rd param to the transformResponse function since angular 1.3, but it was never documented properly in the $resource docs.
I agreed responseHeaders() function will only return response's header,but you can custom it and it's useful anyway.
1.
To solve you problem. With the following:($$service is my $resource instance.)
var serve = new $$service();
serve.id = "hello_wrongPath"; // wrong path,will return 404
serve.$get()
.then(function (data) {
console.log("~~~hi~~~");
console.log(data);
return data;
})
.catch(function (error) {
console.log("~~~error~~~");
console.log(error);
console.log(error.status); // --> 404
console.log(error.statusText); // --> "Not Found"
console.log(error.config.timeout); // --> 5000
console.log(error.config.method); // --> GET
console.log(error.config.url); // --> request url
console.log(error.headers("content-type"));// --> "text/plain"
return error.$promise;
})
.finally(function(data){
console.log("~~~finally~~~");
console.log(data); // --> undefined
});
In this way,u can only catch status,statusText,timeout,method,headers(same with responseHeaders) in ERROR response.
2.
If you want to see response details in success response,I used a interceptor like this:
ng.module("baseInterceptor", [])
.factory("baseInterceptor", ["$q", function ($q) {
return {
'request': function (config) {
console.info(config);
//set timeout for all request
config.timeout = 5000;
return config;
},
'requestError': function (rejection) {
console.info(rejection);
return $q.reject(rejection);
},
'response': function (response) {
console.log("~~interceptor response success~~");
console.log(response);
console.log(response.status);
console.log(response.config.url);
return response;
},
'responseError': function (rejection) {
console.log("~~interceptor response error~~");
console.log(rejection);
console.log(rejection.status);
return $q.reject(rejection);
}
};
}]);
and then add interceptor to module:
.config(["$httpProvider", function ($httpProvider) {
$httpProvider.interceptors.push("baseInterceptor");
}])
You can get response status like this:
$http.get(url).then(function(response){
console.log(response.status); //successful status like OK
}, function(response){
console.log(response.status); //error status like 400-Bad Request
})
I'm using AngularJS v1.5.6, and I do it like this (in my case I put the "getData" method inside a service):
function getData(url) {
return $q(function (resolve, reject) {
$http.get(url).then(success, error);
function success(response) {
resolve(response);
}
function error(err) {
reject(err);
}
});
}
then in the controller (for example), call that like this:
function sendGetRequest() {
var promise = service.getData("someUrlGetService");
promise.then(function(response) {
//do something with the response data
console.log(response.data);
}, function(response) {
//do something with the error
console.log('Error status: ' + response.status);
});
}
As documentation says, the response object has these properties:
data – {string|Object} – The response body transformed with the transform functions.
status – {number} – HTTP status code of the response.
headers – {function([headerName])} – Header getter function.
config – {Object} – The configuration object that was used to generate the request.
statusText – {string} – HTTP status text of the response.
See https://docs.angularjs.org/api/ng/service/$http
Hope it helps!
I think the right answer is a combination of Bardiel's and Ara's answers.
After adding an interceptor inside your resource declaration. Like this:
var resource = $resource(url, {}, {
get: {
method: 'GET'
interceptor: {
response: function(response) {
var result = response.resource;
result.$status = response.status;
return result;
}
}
}
});
Use it as below:
RestAPI.save()
.query(function(response) {
// This will return status code from API like 200, 201 etc
console.log(response.$status);
})
.$promise.catch(function(response) {
// This will return status code from server side like 404, 500 etc
console.log(response.status);
});
I had faced the similar problem.I looked into the angular lib and added a few lines to have status returned in the response itself.In this file,find where promise is being returned.
Replace code block starting with
var promise = $http(httpConfig).then(function(response)
with the following
var promise = $http(httpConfig).then(function(response) {
var data = response.data,
promise = value.$promise;
if (data) {
// Need to convert action.isArray to boolean in case it is undefined
// jshint -W018
if ( angular.isArray(data) !== (!!action.isArray) ) {
throw $resourceMinErr('badcfg', 'Error in resource configuration. Expected ' +
'response to contain an {0} but got an {1}',
action.isArray?'array':'object', angular.isArray(data)?'array':'object');
}
// jshint +W018
if (action.isArray) {
value.length = 0;
forEach(data, function(item) {
value.push(new Resource(item));
});
} else {
copy(data, value);
value.$promise = promise;
}
}
value.status = response.status;
value.$resolved = true;
response.resource = value;
return response;
}, function(response) {
value.status = response.status;
value.$resolved = true;
(error||noop)(response);
return $q.reject(response);
});
or you can add this line
value.status = response.status;
and then access status in code like reponse.status.Though,this is kind of hack but worked for me.I also had to make changes in the minified version.

Resources