Angularjs(1.3.6+) $resource interceptors and global 401 handlers - javascript

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

Related

Why is Angularjs responseError interceptor returning a status code of -1

I'd like to be able to handle the 401 status code in my angularjs project. I've attempted several different variations of response interceptors, but all of them return a code of -1. I'd like to understand why this is happening. Using Angularjs v1.5.8.
myApp.factory('httpResponseInterceptor', function () {
return {
responseError: function (config) {
console.log(config.status);
//The above prints out -1 to the console
return config;
}
};
});
myApp.config(function ($httpProvider) {
$httpProvider.interceptors.push('httpResponseInterceptor');
});
It is CORS issue, CORS is Cross-Origin Resource Sharing.
You must enable CORS from server side.

AngularJs: how to hold all the $http request if its thrown unauthorised request error refresh token and resume all the request

i have developed single page application in angularjs. i have implemented the refresh token mechanism. refresh token suppose to refresh every 30 minutes. I am trying to handle refresh token in responseError of interceptor. I m trying to hold request if it returns 401 unauthorised error. Is there any mechanism to hold all the request once it return 401 error then refresh token and resume all request with new token.
Is it right way to handle the refresh token, here is sample code
$provide.factory('httpTokenInterceptor', function ($q, $injector, $cookies) {
return {
// On request sending
request: function (config) {
config.headers = config.headers || {};
// get this data from $cookies
var globals = $cookies.getObject('globals') || {};
//console.log(globals);
if (globals.authData)
config.headers.Authorization = 'Bearer ' + globals.authData.access_token;
return config;
},
// On response failure
responseError: function (rejection) {
console.log('AuthTokenHttpInterceptor responseError');
console.log(rejection);
if (rejection.status === 401) {
//hold current and all pending request
var aService = $injector.get('authenticationService');
aService.getRefreshToken().then(function(response) {
//need to resume all the request here
deferred.resolve(response);
});
return deferred.promise;
}
return $q.reject(rejection);
}
};
});
In short, you don't want to hold up any of your HTTP calls like that.
Your solution will go and refresh your token after one of your HTTP calls already failed. Also, just to be clear, your code is adding Authorization header even on HTTP calls that are getting resources like HTML templates. If you don't want to do this, then you should restrict that as well.
For one solution, check out this link. It doesn't use any particular library for handling JWT tokens, but you will have to create a wrapper around this implementation to use it wherever you need to do a HTTP call.
My suggestion (and personal preference when handling JWT tokens) is using the angular-jwt library. It's really easy to set up and you can check it out here.
There more complex libraries like auth0, which can do a lot of other stuff, and can be used in conjuction with angular-jwt library. Check out this link to see how to handle token refreshing both prior to a HTTP call and on page refresh.
Hope this helps.
You can hold requests and resume them using AngularJS Interceptors.
authInterceptor.$inject = ['$q', '$rootScope'];
function authInterceptor($q, $rootScope) {
return {
request: function(config) {
var deferred = $q.defer();
$rootScope.$watch('continue', function(value) {
if(value === true)
deferred.resolve(config);
});
return deferred.promise;
}
};
}
In the above example all of the requests hold until $rootScope.continue becomes true. Otherwise they will wait forever.

Angular $resource always calls success callback

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.

How can I prevent angularJS from squelching script errors?

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.

Angular & Twitter REST API

I've been hunting for a few hours now and can't seem to find any information specific to my setup so here goes.
I'm using the MEAN stack and wanting to use the Twitter API in my angular app. I have all the required keys and trigger a twitter api authentication on the server side using Node, then pass the token I get in response to my angular pages. I was hoping to be able to use this token to make requests to the api from an angular service. The request I'm trying to get working the moment is to fetch a given user's profile object. I've attached my service method below. The error I get when I run it is a 405 method no allowed, no access-control-allow-origin header is present.
angular.module('tms.system').factory('Twitter', ['$log', '$q', '$http', '$window', 'twitter', 'Global', function($log, $q, $http, $window, twitter, Global) {
return {
findProfile: function(handle) {
var deferred = $q.defer();
var config = {
timeout:3000,
headers: {
'Authorization': 'Bearer ' + Global.twitterToken,
'X-Testing' : 'testing'
}
};
$http.get('https://api.twitter.com/1.1/users/show.json?screen_name=' + handle, config).
success(function(data) {
$log.info(data);
deferred.resolve(data);
}).
error(function(status) {
$log.error(status);
});
return deferred.promise;
}
};
}]);
Just for future reference, as stated in the comments of maurycy's answer {and being myself trying to get tweets just from Angular without succes}, the best approach for this would be to get them from some backend.
I believe you should use $http.jsonp with a JSON_CALLBACK to get it to work, it's not going to happen with $http.get for sure

Categories