I have an issue with Angular $resource. I use it with our API server with CORS enabled.
No matter what error code server returns (tried 500, 422, 409) resource always calls success callback.
Action in the controller:
#SpacePostsService.save { spaceId: #space._id }, #post, #_createSuccess, (errors)=>¬
#_createError(errors, form)
Where SpacePostsService:
SpacePostsService = $resource postsUrl, {},¬
query:¬
isArray: true¬
method: 'GET'¬
responseType: 'json'¬
transformResponse: (data, headers)->¬
results = data.results || data¬
results = [results] unless _.isArray(results)¬
results
Does anyone have an idea what I am doing wrong?
PS. For readability purposes I didn't show all the code, SpacePostService is a factory and it's properly injected in to the controller.
Finally I've found the issue. It turns out I had global interceptor defined in other place:
AjaxInProgress =
request: (config)->
$rootScope.$broadcast 'ajax:in-progress'
config
response: (response)->
$rootScope.$broadcast 'ajax:finished'
response
responseError: (response)->
$rootScope.$broadcast 'ajax:finished'
return response
The last line in responseError method is the most important one - because I didn't reject the promise so Angular thought I recovered from error and it called success callback in the $resource function. I changed last line in the AjaxInProgress to:
responseError: (response)->
$rootScope.$broadcast 'ajax:finished'
$q.reject(response)
It solved the problem.
Thank you guys for your help.
Related
I am confused why this is happening. I am fairly new with AngularJS/JS so bear with me. I want to create a simple side project that will assist me in learning how to cook, so I am using a recipe database API caled Yummly that will GET recipes with ingredients and other information.
Here's the documentation: https://developer.yummly.com/documentation.
I tried putting api key and ID in the URL as the API said I can. I also tried putting it in the HTTP headers as the API also said I could. I tried both in postman and both worked, but neither work in my code. I get the error:Cannot GET /[object%20Object]. So I am lost. Perhaps this description from the "How to use GET method" from the documentation is the reason?:
Don’t forget to URL-encode parameter names and values (i.e., [ and ] become %5B and %5D). I don't understand this preceding sentence so...
Here's my code. Thanks so much. Much appreciated!
angular.module("app", [])
.controller('AppController', ['$scope', '$http', function ($scope, $http) {
$scope.greeting = 'Hola!';
$scope.getRecipe = function(){
$http.get({
url: 'http://api.yummly.com/v1/api/recipes?q=onion+soup',
method: 'GET',
headers: {
'X-Yummly-App-ID':'myID (secret)',
'X-Yummly-App-Key':'myKey (secret)'
}
}).success(function(response) {
console.log("I got the response.");
}).error(function(error) {
console.log("failed", error);
})
}
}]);
If $http.get is preferred, the code would be:
$http.get('http://api.yummly.com/v1/api/recipes', {
params: {
q: 'onion+soup'
}
headers: {
'X-Yummly-App-ID':'myID (secret)',
'X-Yummly-App-Key':'myKey (secret)'
}
}).success(function(response) {
console.log("I got the response.");
}).error(function(error) {
console.log("failed", error);
})
According to the official document, the syntax of $http.get is:
get(url, [config]);
Shortcut method to perform GET request.
The first parameter of $http.get is a URL string. If an object is passed (as in the question), it would be converted to string, that's why you get the request sent to /[object%20Object] -- [object%20Object] is the toString() result of the config object.
Don’t forget to URL-encode parameter names and values (i.e., [ and ] become %5B and %5D). I don't understand this preceding sentence so...
Don't worry. This will be handled automatically. If the value of q above is 'onion+soup[xxx]', it would be URL-encoded as 'onion%2Bsoup%5Bxxx%5D'.
I need to append the necessary HMAC headers to a request. This should not be very difficult however I am starting to get frustrated. What is wrong with the following code. The actual http call I am doing works; I have run this call myself and it returns the necessary data. It does not work inside the interceptor.
I merely want to get the current implementation working before I add whitelist or blacklist and other customizable data for this interceptor. This is not a question about hmac however but with promises.
The error in this interceptor is with the entire promise line starting at $http(...). When i remove this block and use it as is (minus promise execution) it works fine. As soon as i uncomment the line it gets stuck in a loop and crashes chrome. Everywhere I have read says this is how it is done, but this clearly does not work.
function requestInterceptor(config){
var $http = $injector.get('$http');
var deferred = $q.defer();
$http.get(hmacApiEndpoint, {cache: true}).then(function(data){
console.log('HMAC - Success', data)
deferred.resolve(config)
}, function(config){
console.log('HMAC - Error', config)
deferred.resolve(config)
})
return deferred.promise;
}
return {
request: requestInterceptor
};
Does this have something to do with the fact that angulars $http promise is a different implementation than that of '$q'?
It doesn't look like you are actually amending the config with the newly obtainted HMAC.
Also, you'd need to protect against your requestInterceptor intercepting the call to obtain the HMAC, thus resulting in an infinite loop.
And lastly, you don't need deferred here - just return the promise produced by $http (or $http.then()):
function requestInterceptor(config){
var $http = $injector.get('$http');
// just return, if this is a call to get HMAC
if (config.url === hmacApiEndpoint) return config;
return $http.get(hmacApiEndpoint, {cache: true})
.then(function(response){
console.log('HMAC - Success', response.data)
// not sure where the HMAC needs to go
config.headers.Authorization = response.data;
return config;
})
.catch(function(){
return $q.reject("failed to obtain HMAC");
});
}
return {
request: requestInterceptor
};
My question is about $resource's interceptor(responseError). I want to emphasize that the angularjs I based on is V1.3.6.
Problem:
app.factory('authInterceptor',['$q', '$location', '$log', function($q, $location, $log){
$log.debug('this is a regular factory with injection');
return {
responseError: function(response){
console.log(response)
// problem is that I cant check 401 by response.status,
// because the response here is AN ERROR OBJECT like `SyntaxError: ...`. Anyway to get the status?
return $q.reject(response);
}
}
}])
When I get 401 response, responseError's arguments is AN ERROR OBJECT like SyntaxError: Unexpected token U because the reponse from server is plain text Unathorized with status 401.
But I want to get the response.status, and do something if it is 401.
Any help will be appreciated.
This question should be closed because I finally found answer myself.
When response is 401/404 and anything besides 200, transformResponse still executed and an error occurred! This error just cover the normal response(has status property), so I never get the original response inside interceptor!
I think it's stupid to execute transformResponse if the response's status is not 200! And inside transformResponse, you can't access status code...
Here is a simple interceptor that handles 401s, as well does some configuration:
angular.module('notesApp', [])
.factory('AuthInterceptor',['AuthInfoService', '$q', function(AuthInfoService, $q) {
return {
responseError: function(responseError) {
if (responseError.status === 401) { // authentication issue
//redirect user to login or do something else...
}
return $q.reject(responseError);
}
};
}])
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('AuthInterceptor');
}]);
** Here is an interceptor that only intercepts incoming responses with a non-200 status code.**
If the status code is 401, the user is redirected to the login page. In this case, the promise is rejected so that the controller or service still sees a failure
On my AngularJS sample project, I know that I have a service method which SHOULD throw a JavaScript error.
Using Firebug, I confirm that a JS error is thrown when resolving a promise from a $resource (TypeError: phone.images is undefined); but, the error never appears in the Firebug console.
How can I get the resource to 'fail fast' and propagate the error up the call stack?
Here is the service code:
var phonecatServices = angular.module('phonecatServices', ['ngResource']);
phonecatServices.factory('Phone', ['$resource',
function($resource){
return $resource('phones/:phoneId.json', {}, {
query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
});
}]);
Here is the controller (which fails silently):
phonecatControllers.controller('PhoneDetailCtrl', ['$scope', '$routeParams', 'Phone',
function($scope, $routeParams, Phone) {
$scope.phone = Phone.get({phoneId: $routeParams.phoneId}, function(phone) {
//JS error SHOULD be thrown here:
$scope.mainImageUrl = phone.images[0];
});
...
}]);
I don't want the code to fail silently! How can I fix it?
Ideally, I would like to fix it throughout the framework, rather than putting in special error handling code for each service or resource call.
You need to add the error callback to your get call:
Phone.get({phoneId: $routeParams.phoneId}, function(phone){
// Do Stuff with phone object
}, function(error) {
alert("Y U NO RETURN PHONE?");
// Handle error accordingly
});
Here is the documentation for $resource
If you'd like to generically handle errors for AJAX requests through the angular framework, then you'd probably like something like an http interceptor (Check the interceptors section). This kind of paradigm requires that all requests pass through your interceptor service to be handled in a generic fashion.
I have the following mechanism (ignore the simplicity for the moment please) when a user clicks on a button on a dead simple login form:
$scope.login = function () {
$http.post('/rest/authenticate', {
userName: $scope.userName,
password: $scope.password
}).
success(function (data, status, headers, config) {
//handle success, etc
}).
error(function (data, status, headers, config) {
//hanlde errors, etc
});
};
The method is calling a REST service that could return a String as an answer if the proper credentials are provided or an error (400, 401) if they are not good. The success method gets executed when the good user/pass is given and the error function also executes if the REST call returns some HTTP error.
My problem is that if any kind of error happens, BEFORE the error method gets executed, angular displays the error in the browser console like this:
POST http://server.address/rest/authenticate 400 (Bad Request)
This originates from angular.js (line 8113):
xhr.send(post || null);
It even displays it before a http interceptor's 'responseError' method. Using non-compressed angular version 1.2.14
How can I turn this unnecessary logging off?
zeroflagL's comment is valid and I found no solution to this "issue"