My Question is a bigger and broader version of this question. I want to intercept all http requests issued inside an AngularJS function. Later I need to alter the request URL and than pass it to the server..
How can I do that ? So far I have used $httpProvider and $q to create a interceptor but I am only able to intercept only $http requests not all the requests i.e. if someone clicks on any href link on my page etc. My interceptor code is :-
// register the interceptor as a service
myModule.factory('myHttpInterceptor', function ($q) {
return {
// optional method
'request': function (config) {
// do something on success
console.log("request success");
return config;
},
// optional method
'requestError': function (rejection) {
// do something on error
console.log("Request Error");
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
},
// optional method
'response': function (response) {
// do something on success
console.log("Response received");
return response;
},
// optional method
'responseError': function (rejection) {
// do something on error
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
}
};
});
myModule.factory('myInterceptor', ['$log', function ($log) {
$log.debug('$log is here to show you that this is a regular factory with injection');
var myInterceptor = {
};
return myInterceptor;
}]);
myModule.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push('myHttpInterceptor');
}]);
Intercepting navigation to other pages is different from intercepting http requests. Maybe what you want is to intercept $location changes.
Have a read through this. You can do it but it depends on where the location changes are to.
http://uiadventures.wordpress.com/2013/09/06/routechange-angularjs/
Related
I have a simple Interceptor in angular that intercepts requests and adds a authorization header. It also intercepts a response error of 401 to know if the request failed because of authorization.
Unfortunately it seems to mess with $resource, because my $resource calls ALWAYS return the success callback and never an error (be it 400 or 500 whatever).
It's definitly the interceptor, because if I remove it, the $resource calls return with the correct callback.
Any ideas on how to fix this behavior?
Here's the interceptors request:
function request(config) {
var token = 'whatever-my-token-is';
if (token) {
config.headers.authorization = token;
}
return config;
}
And the responseError:
function responseError(response) {
if (response.status === 401) {
$rootScope.$broadcast('unauthorized');
}
return response;
}
Any help appreciated
I think you need to use a promise to return the error.
adding $q to your Interceptor factory.
like so
$provide.factory('MyHttpInterceptor', function ($q){
...
})
and then have the responseError()
function responseError(response) {
if (response.status === 401) {
$rootScope.$broadcast('unauthorized');
}
return $q.reject(response);
}
this link might help also https://djds4rce.wordpress.com/2013/08/13/understanding-angular-http-interceptors/
In my application I have a grid which fetches data via HTTP promises. At the moment I can detect errorCallbacks - as below:
myDataPromise.then(function () {
//do stuff
}, function errorCallback(response) {
if (response.statusText === "Not Found") {
//do stuff
}else if(response.statusText === "Internal Server Error"){
//do stuff
}
but say for an SSL error, where chrome fires back "ERR::CONNECTION REFUSED" I cannot pick up on this by reading the string as I can with 404's etc. Really what I want is to display a simple image/text stating that there has been an error in retrieving one's data no matter what it is. So if a http GET fails at all - the user knows. This seems to me like a fairly typical spec but I can't find much online regarding it.
You have to create a interceptor
// register the interceptor as a service
$provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
return {
// optional method
'request': function(config) {
// do something on success
return config;
},
// optional method
'requestError': function(rejection) {
// do something on error
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
},
// optional method
'response': function(response) {
// do something on success
return response;
},
// optional method
'responseError': function(rejection) {
// do something on error
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
}
};
});
$httpProvider.interceptors.push('myHttpInterceptor');
// alternatively, register the interceptor via an anonymous factory
$httpProvider.interceptors.push(function($q, dependency1, dependency2) {
return {
'request': function(config) {
// same as above
},
'response': function(response) {
// same as above
}
};
});
The interceptor intercepts all the $http traffic. Even when you are loading templates.
I have a simple scenario - I wish to init my http calls with interceptor that will add a value in headers (a token of some kind).
The problem is that the token is received via http as well (it should be the first call) but I don't know how to make all other calls to wait for it to finish before issuing their own calls...
.factory('sessionData', function () {
var currentToken = '[uninitialized-token]';
return {
getToken: function () {
return currentToken;
},
setAuthData: function (token) {
currentToken = token;
}
}
})
.factory('sessionInjector', ['sessionData', function (sessionData) {
var sessionInjector = {
request: function (config) {
console.log("sending with token: " + sessionData.getToken());
config.headers['x-header-sessionID'] = sessionData.getToken();
}
};
return sessionInjector;
}])
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push('sessionInjector');
}])
.run(['$http', 'configs', 'sessionData', function ($http, configs, sessionData) {
$http.get(configs.authApiUrl + 'login').then(function (ret) {
sessionData.setAuthData(ret);
console.log("successfully authenticated with token " + sessionData.getToken());
});
}])
.controller('TestCtrl', function($http){
$scope.p1 = 'Uninitialized';
$http.get('http://localhost/api/getData').then(function(ret){
$scope.p1 = ret;
});
});
The problem is that the TestCtrl issues an http call before the run method finished getting the token (resulting in header value having the [uninitialized-token] in it's value).
How to make the controllers wait for the 'run' async methods to finish?
$http interceptors can be used to return promises in their callbacks. You can use this to intercept each call and delay it until the promise is resolved.
You should understand how promises work for this.
Example:
myModule.factory('tokenPromise', function($http) {
return $http.get({url: 'myurl/token', bypassToken: true}).then(function(data) {
// This is when your token webservice return, deal with the response here
return data.token;
});
});
myModule.factory('myHttpInterceptor', function($q, tokenPromise) {
return {
'request': function(config) {
if (config.bypassToken) return config;
// This ensures the token promise is resolved before proceeding with the request.
return tokenPromise.then(function(token) {
config.headers['x-header-sessionID'] = token;
return config;
});
},
};
});
myModule.config(function($httpProvider) {
//wire the interceptor here
$httpProvider.interceptors.push('myHttpInterceptor');
})
reference: http service on angular official docs
I am doing a custom Oauth2 authentication module. I have a requestExecutor factory:
$http({
url: requestObject.url,
method: requestObject.method,
cache: requestObject.cache,
headers: requestObject.headers,
data: requestObject.data})
.then(function (resData) {
if (requestObject.callback) {
requestObject.callback(resData.data);
}
}, function () {
if (requestObject && requestObject.customErrorCallback) {
requestObject.customErrorCallback();
}
});
and Http Interceptor:
'responseError': function (rejection) {
console.log(rejection)
switch (rejection.status) {
case 401 :
{
$rootScope.$emit(CONST.UNAUTHORIZED);
break;
}
case 403 :
{
$rootScope.$emit(CONST.FORBIDDEN, rejection.config);
break;
}
}
return $q.reject(rejection);
}
So when I execute a request and get 403 response error
from the server I want to send the full request to the listener of CONST.FORBIDDEN (the only thing that is missing is the callbacks from the $http().then() promise). The reason is that I want to execute the failed request again after I finish with the refreshing the access token.
My questions are:
Where are stored $http().then() promises?
Can to get $http().then() promises?
How can I implement 2.?
No sure about your intention, there would probably be better solutions, but you can register the callback handlers in the config object and access them in the interceptor. plnkr
But here is a better design. angular-app/securityInterceptor
You will do the refreshing in the interceptor and execute the request again. If the second request was successful you return the result, which will be processed by your success/error-handlers.
$scope.fetchAsync = function(forceError) {
function successHandler(result) {
$scope.content = result.data;
}
function errorHandler(result) {
$scope.content = result;
}
$scope.content = 'Loading...';
$http.get(forceError ? 'not-found' : 'test.txt', {
handler: {
success: successHandler,
error: errorHandler
}
}).then(successHandler, errorHandler);
}
$httpProvider.interceptors.push(function($q) {
return {
'responseError': function(rejection) {
console.log(rejection.config.handler);
return rejection;
}
};
});
I have a angular application with many $http request and i want redirect the users on the login page if the server session expires (get 401). Does anyone know a solution witch works for all $http without adding .error() on every $http?
It would be better if you could use an http interceptor to redirect all detected 401 errors.
// add an http interceptor via app.config
app.config(function($$httpProvider) {
$httpProvider.interceptors.push('my401Detector');
});
// interceptor logic.
app.factory('my401Detector', function($location, $q) {
return {
responseError: function(response) {
if(response.status === 401) {
$location.path('/login');
return $q.reject(response);
}
else {
return $q.reject(response);
}
}
};
});
You can use Interceptors to achieve this. From Mean.js source code
angular.module('users').config(['$httpProvider',
function($httpProvider) {
// Set the httpProvider "not authorized" interceptor
$httpProvider.interceptors.push(['$q', '$location', 'Authentication',
function($q, $location, Authentication) {
return {
responseError: function(rejection) {
switch (rejection.status) {
case 401:
// Deauthenticate the global user
Authentication.user = null;
// Redirect to signin page
$location.path('signin');
break;
case 403:
// Add unauthorized behaviour
break;
}
return $q.reject(rejection);
}
};
}
]);
}
]);