Angularjs $http interceptor pause async requests if login required - javascript

I have a $http in my angularjs app.
If my api returns an unauthorised response, I show the login.
Once the user has logged in via a modal, the request interceptor then retries the $http request.
app.factory('LoginInterceptor', function ($injector, $timeout, $q) {
return {
request: function (config) {
return config;
},
response: function (response) {
var LoginModalService = $injector.get('LoginModalService'),
$http = $injector.get('$http'),
$location = $injector.get('$location'),
$rootScope = $injector.get('$rootScope'),
url = response.config.url.split('?')[0];
if (response.data && response.data.token_status <= 0) {
return $timeout(angular.noop, 1000).then(function () {
return LoginModalService.show();
}).then(function () {
return $http(response.config);
}, function () {
$location.path('/login');
return $q.reject(response);
});
}
return response || $q.when(response);
}
}
});
The problem is, if the multiple requests fire asynchronously the login is shown for each request.
How can I stop all other requests from executing until the user has signed in? This currently works when a single request is made. I don't want to cancel all other requests, I want them to resolve once the user has signed in.

you could set a flag on the first unauthorized request returned, and reject all following unauthorized request/response:
app.factory('LoginInterceptor', function ($injector, $timeout, $q) {
var isAuthorized = true;
return {
request: function (config) {
if(isAuthorized){
return config
}else{
return $q.reject(config);
}
},
response: function (response) {
var LoginModalService = $injector.get('LoginModalService'),
$http = $injector.get('$http'),
$location = $injector.get('$location'),
$rootScope = $injector.get('$rootScope'),
url = response.config.url.split('?')[0];
if (response.data && response.data.token_status <= 0) {
if(isAuthorized){
isAuthorized = false;
return $timeout(angular.noop, 1000).then(function () {
return LoginModalService.show();
}).then(
return $http(response.config);
}, function () {
$location.path('/login');
return $q.reject(response);
});
}else{
return $q.reject(response)
}
}
return response || $q.when(response);
}
}
});

Related

Angularjs, function not being properly invoked inside controller

I'm doing this login exercise where users can login and post notes, and view the notes that they've posted. My problem is when I logout and login with a different user I see the notes from the previous user.
Here's an illustration:
I log in with a different user then this shows up:
I restart the page and the appropriate note shows up:
The controller for this:
exports.homeController = function ($scope, $location, $q, $users, $window, $notes, $http) {
var auth = function () {
var userInfo = $users.getUserInfo()
if (userInfo) {
return $q.when(userInfo)
} else {
return $q.reject({ authenticated: false })
}
}
$scope.userInfo = auth()
myNotes($scope.userInfo.$$state.value.accessToken) // I invoke my function to get the notes for each specific user but it doesn't seem to work.
$scope.logout = function () {
$users.logout()
.then(function (results) {
$scope.userInfo = null
$scope.myNotes = null
$location.path('/')
}, function (err) {
console.log(err)
})
}
$scope.notes = {
notes: ''
}
$scope.postNote = function () {
$notes.postNotes($scope.userInfo.$$state.value.accessToken, $scope.notes)
.then(function (result) {
$scope.myNotes.push($scope.notes)
$scope.notes = ''
}, function (err) {
console.log(err)
})
}
function myNotes (user_id) {
$notes.getMyNotes(user_id)
.then(function (result) {
console.log(result)
$scope.myNotes = result.data
}, function (err) {
console.log(err)
})
}
}
This is the app https://login-sys.herokuapp.com/
I've found your non-minified code for the services.
Based on that I think the problem is that you declare var deferred = $q.defer() one time in the $notes service.
I think it should be "renewed" every time the service methods are called:
function getMyNotes (user_id) {
var deferred = $q.defer();
$http.get('/api/myNotes/' + user_id + '?access_token=' + user_id)
.then(function (result) {
deferred.resolve(result)
}, function (err) {
deferred.reject(err)
});
return deferred.promise
}
Similarly in postNotes.
The second time you return the same promise with the same value, so your homeController's getMyNotes function will get the same result despite the $notes service making a new request.
In the $users service's logout and signup functions you are already using it correctly.

Angularjs FB login using factory

I am new to angularjs.I am using factories where i have written the fb login code.
And during the last step i am sending all the data to my server where the user is registered in my database and the token is sent.
Here is the code.
'use strict'
APP.factory('authenticationFactory',['ENV','$http','$rootScope', function (ENV,$http,$rootScope) {
return {
socialLogin:function(data){
return $http.post($rootScope.apiURL+'sociallogin',data).then(function (resp) {
if(resp.status == 200) {
return resp.data;
}
})
},
fbLogin: function () {
var FB = window.FB;
var scopes = 'public_profile,email';
var that = this;
FB.login(function (response) {
return that.facebookStatusChangeCallback(response);
}, {scope: scopes});
},
facebookStatusChangeCallback: function(response){
if (response.status === 'connected') {
// Logged into your app and Facebook.
var r = this.facebookApiRequest(response);
console.log(r);
} else if (response.status === 'not_authorized') {
// The person is logged into Facebook, but not your app.
console.log('Please log into this app.');
} else {
// The person is not logged into Facebook, so we're not sure if
// they are logged into this app or not.
console.log('Please log into Facebook.');
}
},
facebookApiRequest: function (authResponse) {
var that = this;
var r = FB.api('/me?fields=id,name,email,gender,first_name,last_name,age_range,link,birthday', function (response) {
var r = FB.api("/" + response.id + "/picture?height=720", function (pictureResponse) {
if (pictureResponse && !pictureResponse.error) {
/* handle the result */
response.profile_pic = pictureResponse.data.url;
response.access_token = authResponse.authResponse.accessToken;
response.provider = 'facebook';
response.devicetoken = '';
response.full_name = response.first_name+' '+response.last_name;
var r = that.socialPluginLogin(response).then(function (resp) {
return that.resp;
});
return r;
} else {
console.log('error while fatching fb pic');
}
});
console.log(r);
});
console.log(that);
},
socialPluginLogin : function (data) {
var resp = this.socialLogin(data).then(function (resp) {
return resp;
});
return resp;
}
};
}]);
I am calling the fbLogin() function from my controller. i need the response from the function socialLogin() so that i can change the state.
Where am i going wrong.??
The answer was pointing in the wrong direction, another try:
Your function fbLogin should return a promise, which can be resolved by socialLogin later. Since fbLogin doesn't return a thing, you don't receive any signal from the completed login.
See this:
// We add $q here
APP.factory('authenticationFactory',['ENV','$http','$rootScope','$q', function (ENV,$http,$rootScope,$q) {
var loginPromise;
return {
socialLogin:function(data){
return $http.post($rootScope.apiURL+'sociallogin',data).then(function (resp) {
if(resp.status == 200) {
// This is your connection to the controller
loginPromise.resolve(resp.data);
return resp.data;
}
})
},
fbLogin: function () {
var FB = window.FB;
var scopes = 'public_profile,email';
var that = this;
FB.login(function (response) {
return that.facebookStatusChangeCallback(response);
}, {scope: scopes});
// Create and return a promise
loginPromise = $q.defer();
// EDIT: My fault, return the promise:
return loginPromise.promise;
},
//...
And add this to the controller:
authenticationFactory.fbLogin().then(function(data){
// Check it out:
console.dir(data);
})
Additional things you should consider:
Define your functions in the function body, not in the return statement. You can eliminate that=this this way
Only return the API, not all the functions
Read up on promises, they are the way to go in the angular world. You might as well use callbacks, but those are tedious to handle.
Change your socialLogin function to below, your function would return a promise object which you can consume in socialPluginLogin via then which you are already doing.
socialLogin:function(data){
return $http.post($rootScope.apiURL+'sociallogin',data)
},

Intercept Unathorized API calls with Angular

I am trying to intercept the 401 and 403 errors to refresh the user token, but I can't get it working well. All I have achieved is this interceptor:
app.config(function ($httpProvider) {
$httpProvider.interceptors.push(function ($q, $injector) {
return {
// On request success
request: function (config) {
var deferred = $q.defer();
if ((config.url.indexOf('API URL') !== -1)) {
// If any API resource call, get the token firstly
$injector.get('AuthenticationFactory').getToken().then(function (token) {
config.headers.Authorization = token;
deferred.resolve(config);
});
} else {
deferred.resolve(config);
}
return deferred.promise;
},
response: function (response) {
// Return the promise response.
return response || $q.when(response);
},
responseError: function (response) {
// Access token invalid or expired
if (response.status == 403 || response.status == 401) {
var $http = $injector.get('$http');
var deferred = $q.defer();
// Refresh token!
$injector.get('AuthenticationFactory').getToken().then(function (token) {
response.config.headers.Authorization = token;
$http(response.config).then(deferred.resolve, deferred.reject);
});
return deferred.promise;
}
return $q.reject(response);
}
}
});
});
The issue is that the responseError does an infinite loop of 'refreshes' because by Authorization header with the updated token, that is not being received by $http(response.config) call.
1.- App has an invalid token stored.
2.- App needs to do an API call
2.1 Interceptor catch the `request`.
2.2 Get the (invalid) stored token and set the Authorization header.
2.3 Interceptor does the API call with the (invalid) token setted.
3.- API respond that used token is invalid or expired (403 or 401 statuses)
3.1 Interceptor catch the `responseError`
3.2 Refresh the expired token, get a new VALID token and set it in the Authorization header.
3.3 Retry the point (2) with the valid refreshed token `$http(response.config)`
The loop is happening in point (3.3) because the Authorization header NEVER has the new refreshed valid token, it has the expired token instead. I don't know why because it supposed to be setted in the responseError
AuthenticationFactory
app.factory('AuthenticationFactory', function($rootScope, $q, $http, $location, $log, URI, SessionService) {
var deferred = $q.defer();
var cacheSession = function(tokens) {
SessionService.clear();
// Then, we set the tokens
$log.debug('Setting tokens...');
SessionService.set('authenticated', true);
SessionService.set('access_token', tokens.access_token);
SessionService.set('token_type', tokens.token_type);
SessionService.set('expires', tokens.expires);
SessionService.set('expires_in', tokens.expires_in);
SessionService.set('refresh_token', tokens.refresh_token);
SessionService.set('user_id', tokens.user_id);
return true;
};
var uncacheSession = function() {
$log.debug('Logging out. Clearing all');
SessionService.clear();
};
return {
login: function(credentials) {
var login = $http.post(URI+'/login', credentials).then(function(response) {
cacheSession(response.data);
}, function(response) {
return response;
});
return login;
},
logout: function() {
uncacheSession();
},
isLoggedIn: function() {
if(SessionService.get('authenticated')) {
return true;
}
else {
return false;
}
},
isExpired: function() {
var unix = Math.round(+new Date()/1000);
if (unix < SessionService.get('expires')) {
// not expired
return false;
}
// If not authenticated or expired
return true;
},
refreshToken: function() {
var request_params = {
grant_type: "refresh_token",
refresh_token: SessionService.get('refresh_token')
};
return $http({
method: 'POST',
url: URI+'/refresh',
data: request_params
});
},
getToken: function() {
if( ! this.isExpired()) {
deferred.resolve(SessionService.get('access_token'));
} else {
this.refreshToken().then(function(response) {
$log.debug('Token refreshed!');
if(angular.isUndefined(response.data) || angular.isUndefined(response.data.access_token))
{
$log.debug('Error while trying to refresh token!');
uncacheSession();
}
else {
SessionService.set('access_token', response.data.access_token);
SessionService.set('token_type', response.data.token_type);
SessionService.set('expires', tokens.expires);
SessionService.set('expires_in', response.data.expires_in);
deferred.resolve(response.data.access_token);
}
}, function() {
// Error
$log.debug('Error while trying to refresh token!');
uncacheSession();
});
}
return deferred.promise;
}
};
});
PLUNKER
I made a plunker & backend to try to reproduce this issue.
http://plnkr.co/edit/jaJBEohqIJayk4yVP2iN?p=preview
Your interceptor needs to keep track of whether or not it has a request for a new authentication token "in flight". If so, you need to wait on the result of the in-flight request rather than initiating a new one. You can do this by caching the promise returned by your AuthRequest and using the cached promise instead of creating a new one for every API requests.
Here is an answer to a similar question that demonstrates this.
For you example - here is an example implementation:
app.config(function ($httpProvider) {
$httpProvider.interceptors.push(function ($q, $injector) {
var inFlightRequest = null;
return {
// On request success
request: function (config) {
var deferred = $q.defer();
if ((config.url.indexOf('API URL') !== -1)) {
// If any API resource call, get the token firstly
$injector.get('AuthenticationFactory').getToken().then(function (token) {
config.headers.Authorization = token;
deferred.resolve(config);
});
} else {
deferred.resolve(config);
}
return deferred.promise;
},
response: function (response) {
// Return the promise response.
return response || $q.when(response);
},
responseError: function (response) {
// Access token invalid or expired
if (response.status == 403 || response.status == 401) {
var $http = $injector.get('$http');
var deferred = $q.defer();
// Refresh token!
if(!inFlightRequest){
inFlightRequest = $injector.get('AuthenticationFactory').refreshToken();
}
//all requests will wait on the same auth request now:
inFlightRequest.then(function (token) {
//clear the inFlightRequest so that new errors will generate a new AuthRequest.
inFlightRequest = null;
response.config.headers.Authorization = token;
$http(response.config).then(deferred.resolve, deferred.reject);
}, function(err){
//error handling omitted for brevity
});
return deferred.promise;
}
return $q.reject(response);
}
}
});
});
UPDATE:
It's not clear to me from your plunk exactly what the problem is, but there is a problem with your AuthenticationService. Recommended changes are below and here is a Plunkr that is a bit more complete (and includes tracking inflight requests):
app.factory('AuthenticationFactory', function($rootScope, $q, $http, $location, $log, URI, SessionService) {
//this deferred declaration should be moved. As it is, it's created once and re-resolved many times, which isn't how promises work. Subsequent calls to resolve essentially are noops.
//var deferred = $q.defer();
var cacheSession = function(tokens) {
SessionService.clear();
// Then, we set the tokens
$log.debug('Setting tokens...');
SessionService.set('authenticated', true);
SessionService.set('access_token', tokens.access_token);
SessionService.set('token_type', tokens.token_type);
SessionService.set('expires', tokens.expires);
SessionService.set('expires_in', tokens.expires_in);
SessionService.set('refresh_token', tokens.refresh_token);
SessionService.set('user_id', tokens.user_id);
return true;
};
var uncacheSession = function() {
$log.debug('Logging out. Clearing all');
SessionService.clear();
};
return {
login: function(credentials) {
var login = $http.post(URI+'/login', credentials).then(function(response) {
cacheSession(response.data);
}, function(response) {
return response;
});
return login;
},
logout: function() {
uncacheSession();
},
isLoggedIn: function() {
if(SessionService.get('authenticated')) {
return true;
}
else {
return false;
}
},
isExpired: function() {
var unix = Math.round(+new Date()/1000);
if (unix < SessionService.get('expires')) {
// not expired
return false;
}
// If not authenticated or expired
return true;
},
refreshToken: function() {
var request_params = {
grant_type: "refresh_token",
refresh_token: SessionService.get('refresh_token')
};
return $http({
method: 'POST',
url: URI+'/refresh',
data: request_params
});
},
getToken: function() {
//It should be moved here - a new defer should be created for each invocation of getToken();
var deferred = $q.defer();
if( ! this.isExpired()) {
deferred.resolve(SessionService.get('access_token'));
} else {
this.refreshToken().then(function(response) {
$log.debug('Token refreshed!');
if(angular.isUndefined(response.data) || angular.isUndefined(response.data.access_token))
{
$log.debug('Error while trying to refresh token!');
uncacheSession();
}
else {
SessionService.set('access_token', response.data.access_token);
SessionService.set('token_type', response.data.token_type);
SessionService.set('expires', tokens.expires);
SessionService.set('expires_in', response.data.expires_in);
deferred.resolve(response.data.access_token);
}
}, function() {
// Error
$log.debug('Error while trying to refresh token!');
uncacheSession();
});
}
return deferred.promise;
}
};
});
As a final note, keeping track of both inflight getToken requests and inflight refreshToken requests will keep you from making too many calls to your server. Under high load you might be creating way more access tokens than you need.
UPDATE 2:
Also, reviewing the code, when you get a 401 error you are calling refreshToken(). However, refreshToken does not put the new token information in the session cache, so new requests are going to continue using the old token. Updated the Plunkr.

How to implement login system in Angular app with already existing REST API backend

My friend and I are building an app - my friend is on the backend (Node.js) and I'm on the front.
He implemented sessions on his end and provided me with the URL I need to call to log in. For example, a POST request
http://ourapp.heroku.com/login
with which username and password are passed.
On my side, in the Angular app, I create a login page which calls an Angular service when Login is clicked. If this service receives a 200 from the server, it does:
$cookieStore.put(cookieNames.LOGGED_IN_COOKIE, true);
$state.go('home', {}, {reload: true});
The problem is that we're having weird issues with the app on the front end. For example logging in and out often don't work. Also, users are able to go to pages even after they log out. I figured out (at least I think) that I'm not properly storing the Cookie I receive from the server, I'm only storing my own.
This whole Angular thing is still weird to me, because in PHP or Python apps you get a page request from the client and verify if he's logged in before sending him the page he requested. In Angular it's different - the user has all of the pages already. So how do I limit what he can see without logging in and how to I properly keep track of the server's cookie?
If you use ui-router, you can do something similar to this:
First introduce some kind of access-levels to your states
$stateProvider
.state('admin', {
url: "/admin",
templateUrl: "/app/views/admin.html",
controller: "AdminController",
data: {
accessLevel: 'admin'
}
})
then you have to check on state change, if your logged in user has the required access-level:
You can create an auth service which implements your logic to log your user in, as example you can use this service
angular.module('app')
.factory("AuthService", ["$rootScope", "$http", "AuthSession", "AuthHttpBuffer", "AUTH_EVENTS", function ($rootScope, $http, AuthSession, AuthHttpBuffer, AUTH_EVENTS) {
function loginFailed() {
$rootScope.$broadcast("auth-change", AUTH_EVENTS.loginFailed);
};
AuthSession.load();
$rootScope.$on('$stateChangeStart', function (event, nextState) {
if (nextState.data && nextState.data.accessLevel && !service.isAuthorized(nextState.data.accessLevel)) {
event.preventDefault();
$rootScope.$broadcast('auth-change', AUTH_EVENTS.loginRequired, nextState.name);
}
});
var service = {
login: function (credentials) {
return $http
.post('/api/account/login', credentials)
.success(function (data, status) {
if ((status < 200 || status >= 300) && data.length >= 1) {
loginFailed();
return;
}
AuthSession.create(data.AccessToken, data.User);
$rootScope.$broadcast("auth-change", AUTH_EVENTS.loginSuccess);
AuthHttpBuffer.retryAll();
}).error(function (data, status) {
loginFailed();
});
},
cancel: function () {
AuthHttpBuffer.rejectAll();
},
logout: function () {
AuthSession.destroy();
$rootScope.$broadcast("auth-change", AUTH_EVENTS.logoutSuccess);
},
isAuthenticated: function () {
return (AuthSession.token !== null);
},
isAuthorized: function (accessLevel) {
if (!accessLevel) return true;
return (this.isAuthenticated() && AuthSession.user.UserRoles.indexOf(accessLevel) !== -1);
}
}
return service;
}]);
and your AuthSession service:
angular.module('app')
.factory("AuthSession", ["$rootScope", "$window", "AUTH_EVENTS", function ($rootScope, $window, AUTH_EVENTS) {
var sessionService = {
user: null,
token: null,
//load the stored session data
load: function () {
var user = ...yourdata... //TODO implement load user data;
var token = ...yourdata... //implement load user data;
if (!user || !token) return;
if (!this.checkTokenExpiration(token)) return;
this.user = user;
this.token = token;
$rootScope.$broadcast("auth-change", AUTH_EVENTS.loginSuccess);
},
//save the current data to the session storage
save: function () {
//TODO save your userdata/token etc.
},
//create the current user with the assosiated token
create: function (token, user) {
this.token = token;
this.user = user;
if (!angular.isArray(this.user.UserRoles))
this.user.UserRoles = [this.user.UserRoles];
this.save();
},
//destroy an user with all assosiated data
destroy: function () {
this.token = null;
this.user = null;
//TODO clear your saved data here
},
//check if the supplied access token data is expired
checkTokenExpiration: function (token) {
if (token === undefined || token === null) return false;
var retval = (new Date(token.TokenExpires).getTime() > new Date().getTime());
if (retval === false) {
sessionService.destroy();
$rootScope.$broadcast("auth-change", AUTH_EVENTS.sessionTimeout);
}
return retval;
}
}
return sessionService;
}]);
and the constants:
angular.module('app')
.constant('AUTH_EVENTS', {
loginSuccess: 'auth-login-success',
loginFailed: 'auth-login-failed',
logoutSuccess: 'auth-logout-success',
loginRequired: 'auth-login-required',
sessionTimeout: 'auth-session-timeout',
notAuthorized: 'auth-not-authorized'
});
If you want be able to catch urls, where you haven't the right accesrights, you can send the request to a http buffer:
angular.module('app')
.factory('AuthHttpBuffer', ["$injector", function ($injector) {
/** Holds all the requests, so they can be re-requested in future. */
var buffer = [];
/** Service initialized later because of circular dependency problem. */
var $http;
function retryHttpRequest(config, deferred) {
function successCallback(response) {
deferred.resolve(response);
}
function errorCallback(response) {
deferred.reject(response);
}
$http = $http || $injector.get('$http');
$http(config).then(successCallback, errorCallback);
}
return {
/**
* Appends HTTP request configuration object with deferred response attached to buffer.
*/
append: function (config, deferred) {
buffer.push({
config: config,
deferred: deferred
});
},
/**
* Abandon or reject (if reason provided) all the buffered requests.
*/
rejectAll: function (reason) {
if (reason) {
for (var i = 0; i < buffer.length; ++i) {
buffer[i].deferred.reject(reason);
}
}
buffer = [];
},
/**
* Retries all the buffered requests clears the buffer.
*/
retryAll: function () {
for (var i = 0; i < buffer.length; ++i) {
retryHttpRequest(buffer[i].config, buffer[i].deferred);
}
buffer = [];
}
};
}]);
and if you haven't enough you can also add an interceptor, that triggers an auth change event, if the server response is unauthorized:
angular.module('app')
.factory('AuthInterceptor', ["$rootScope", "$q", "AuthSession", "AuthHttpBuffer", "AUTH_EVENTS", function ($rootScope, $q, AuthSession, AuthHttpBuffer, AUTH_EVENTS) {
return {
request: function (config) {
config.headers = config.headers || {};
if (AuthSession.token) {
config.headers.Authorization = 'Bearer ' + AuthSession.token.TokenKey;
}
return config;
},
responseError: function (rejection) {
if (rejection.status === 401) {
var deferred = $q.defer();
AuthHttpBuffer.append(rejection.config, deferred);
if (AuthSession.token) {
$rootScope.$broadcast('auth-change', AUTH_EVENTS.notAuthorized);
} else {
$rootScope.$broadcast('auth-change', AUTH_EVENTS.loginRequired);
}
return deferred.promise;
}
return $q.reject(rejection);
}
}
}]);
this interceptor also adds a session token to all requests if available.
to use this interceptor, you have to add the following two lines to your app.config():
$httpProvider.defaults.withCredentials = true;
$httpProvider.interceptors.push("AuthInterceptor");

AngularJs: Exclude some requests from Interceptor

Below is my interceptor which handles global errors. But I want to bypass some http requests. Any suggestions ?
var interceptor = ['$rootScope', '$q',function (scope, $q) {
function success(response) {
return response;
}
function error(response) {
var status = response.status;
if (status == 401) {
window.location = "./index.html#/404";
return;
}
if (status == 0) {
window.location = "./index.html#/nointernet";
}
return $q.reject(response);
}
return function (promise) {
return promise.then(success, error);
}
}];
$httpProvider.responseInterceptors.push(interceptor);
I was able to implement this functionality simply by adding a property to the config object of the $http request. ie. ignore401. Then, in my interceptor, in the response error handler, check for the property on the config object, and if it is present, do not forward to login or whatever else you do on a 401 response.
First, the interceptor:
$provide.factory('authorization', function() {
return {
...
responseError: (rejection) => {
if (rejection.status === 401 && !rejection.config.ignore401) {
// redirect to login
}
return $q.reject(rejection);
}
};
});
Then, for any request that I want to bypass the 401 error handler:
$http({
method: 'GET',
url: '/example/request/to/ignore/401error',
ignore401: true
});
Hope that helps.
I'm solving this as follows:
$httpProvider.interceptors.push(function($rootScope) {
return {
request: function(config) {
var hideUrl = "benefit/getall"; // don't show the loading indicator for this request
var hide = (config.url.indexOf(hideUrl)); // is this isn't -1, it means we should hide the request from the loading indicator
if(hide == -1)
{
$rootScope.$broadcast('loading:show')
}
return config
},
response: function(response) {
$rootScope.$broadcast('loading:hide')
return response
}
}
});
Doesn't the response object contain an "options" property where you can inspect what URL was used in the request?

Categories

Resources