Reputation: 146
I'm developing an Angular webapp which uses an API in order to get some data. To get that data, I must send a JWT. I created a service that calls to a PHP file in order to get the token:
(function () {
'use strict';
angular
.module('MyApp')
.factory('TokenService', Service);
function Service( $http, $localStorage ) {
var service = {};
service.getToken = getToken;
return service;
function getToken() {
return $http.post('gettoken.php');
}
}})();
And what I do is in .run block I call it and store the token received:
function run( $rootScope, $http, $location, $localStorage, TokenService ) {
TokenService.getToken().then(function(response) {
$localStorage.token = response.data.token;
});
console.log('token: ' + $localStorage.token);
$http.defaults.headers.common.Authorization = 'Bearer ' + $localStorage.token;
}})();
Every few minutes the token expires, so I must request for another token so the user can continue interacting with the API.
To solve this, my first approach was to use .config block to intercept 401 error (token expired) and ask for another token, but this is not possible because as far as I know, is not possible to inject services in .config block:
function config2( $httpProvider ) {
$httpProvider.interceptors.push(function ($q, $rootScope) {
return {
'response': function (response) {
//Will only be called for HTTP up to 300
console.log(response);
return response;
},
'responseError': function (rejection) {
if(rejection.status === 401) {
var deferred = $q.defer();
TokenService.getToken(function() {
retryHttpRequest(response.config, deferred);
});
return deferred.promise;
} else {
return response;
}
function retryHttpRequest(config, deferred){
function successCallback(response){
deferred.resolve(response);
}
function errorCallback(response){
deferred.reject(response);
}
var $http = $injector.get('$http');
$http(config).then(successCallback, errorCallback);
}
}
};
});
}
Indeed, when I execute the webapp, it says that TokenService is not defined, and if I inject the TokenService, obviously it creates a circle dependancy:
angular.js:66 Uncaught Error: [$injector:cdep] Circular dependency found: $http <- TokenService <- $http <- $templateRequest <- $route
http://errors.angularjs.org/1.6.4/$injector/cdep?p0=%24http%20%3C-%20TokenService%20%3C-%20%24http%20%3C-%20%24templateRequest%20%3C-%20%24route
at angular.js:66
at getService (angular.js:4936)
at injectionArgs (angular.js:4969)
at Object.invoke (angular.js:4995)
at Object.enforcedReturnValue [as $get] (angular.js:4836)
at Object.invoke (angular.js:5003)
at angular.js:4795
at getService (angular.js:4944)
at injectionArgs (angular.js:4969)
at Object.invoke (angular.js:4995)
That's a very silly situation, but I've spent the whole day trying to find an optimal solution. I tried to simply reload page once the interceptor detects 401 responseError, but this is not optimal, because the user sees the web refreshing and he loses all data inserted for the request is doing.
It would be great if any of you have had to deal with this kind of problem and can share any approach in order to solve it.
Upvotes: 0
Views: 1513
Reputation: 5674
This may be because the $http
service depends on your interceptor, your TokenService
depends on the $http
service, and your interceptor depends on the TokenService
. This causes a circular dependency.
It seems you're already using the $injector
to inject the $http-service just-in-time, so to solve your circular dependency, using the same method to inject your TokenService
might solve your problem.
var TokenService = $injector.get('TokenService');
I've stumbled upon the same problem before, so if it's any help you can check my solution on GitHub.
Upvotes: 2