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.
Related
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.
All of our content pages have a particular header, X-Foo. When the content of the ng-view changes, we want to display the new page's X-Foo header in a different element. How can we get this value whenever the content changes?
EDIT: Since this was apparently unclear, the header is expected in the response, not the request.
You can use a httpInterceptor for this. HTTP interceptors are a great way to define behavior in a single place for how a request or response is handled for all HTTP calls using the $http service
app.config(function ($httpProvider) {
$httpProvider.interceptors.push('httpInterceptor');
}).factory('httpInterceptor', function (liveInterviewConfiguration) {
return {
request : function (request) {
console.info("Your request headers are");
console.info(request.headers);
return request;
},
requestError : function (error) {
return error;
},
response : function (response) {
return response;
},
responseError : function (error) {
return error;
}
};
});
Can you access the headers in the controller with $http? I've nothing that has changing headers readily available to test this with.
controller('SampleCtrl', function($http, $scope) {
// Add headers from $http
$scope.http = $http.defaults.headers.common;
});
Alternatively, if that does not work, you may want to look at using http interceptors.
.config(function($routeProvider, $locationProvider, $httpProvider) {
$httpProvider.interceptors.push(function($q) {
return {
'response': function(response) {
// do something on success
console.log(response);
return response;
}
};
});
}
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);
}
};
});
I have an AngularJS app, and need an ajax loader for every request done by the $http - is there a simple way to do this.
My solution now is to set $rootScope.loading = 1 everytime i call a $http, and on the success set $rootScope.loading = 0..
What is the "best practice" for this?
My code now looks like:
$rootScope.loading = 1;
$http({method : "POST", url:url, data: utils.params(data), headers: {'Content-Type': 'application/x-www-form-urlencoded'}}).success(function() {
$rootScope.loading = 0;
});
In this case will be better to use interceptor
Anytime that we want to provide global functionality on all of our requests, such as authentication,
error handling, etc., it’s useful to be able to provide the ability to intercept all requests before they
pass to the server and back from the server.
angular.module('myApp')
.factory('myInterceptor',
function ($q,$rootScope) {
var interceptor = {
'request': function (config) {
$rootScope.loading = 1;
// Successful request method
return config; // or $q.when(config);
},
'response': function (response) {
$rootScope.loading = 0;
// successful response
return response; // or $q.when(config);
},
'requestError': function (rejection) {
// an error happened on the request
// if we can recover from the error
// we can return a new request
// or promise
return response; // or new promise
// Otherwise, we can reject the next
// by returning a rejection
// return $q.reject(rejection);
},
'responseError': function (rejection) {
// an error happened on the request
// if we can recover from the error
// we can return a new response
// or promise
return rejection; // or new promise
// Otherwise, we can reject the next
// by returning a rejection
// return $q.reject(rejection);
}
};
return interceptor;
});
and register it to the config
angular.module('myApp')
.config(function($httpProvider) {
$httpProvider.interceptors.push('myInterceptor');
});
example from ng-book
Use an http interceptor in order to intercept all your $http requests\responses and perform logic in them.
Here's an example of creating a custom one.
Here's an example of a ready module.
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.