Prevent $http service execution using Interceptor - angularjs

I am building an authentication solution with Angular. I am using Interceptors to validate Request and if there is not valid token prevent from further processing and redirect. Here is my simplified Interceptor:
prism.service('APIInterceptor', function($rootScope) {
var service = this;
service.request = function(config) {
.....
return config;
};
})
Just for the sake of POC that I am working on what would the correct way of stopping this request from any further processing be?
Thanks

From the Angular Docs on Interceptors for the request method:
request: interceptors get called with a http config object. The function is free to modify the config object or create a new one. The function needs to return the config object directly, or a promise containing the config or a new config object.
The rest of the documentation can be found here. From this you can see that the method can also return a promise (which is actually pretty awesome) so you could always reject it.
Try something like this:
prism.service('APIInterceptor', function($q, $rootScope) {
this.request = function(config) {
if( /*config is not valid*/ ) {
return $q.reject({message: 'ERROR, ERROR... INTRUDER ALERT!', status: 401, config: config});
} else {
return config;
}
};
});
And see how it might be handled (I have no idea what your application will do). Let me know if it works out for you!
EDIT: My answer has been accepted, but is incomplete and it will haunt me forever if I don't complete it. So, after writing some test code of my own I've realized that you can do 1 of 2 things in this situation. The first is to handle the unauthorized request in the interceptor:
...
this.request = function(config) {
if(/* config is not authorized */) {
// Do something here like redirect/issue another request... whatever
return $q.reject({/*whatever the hell you want*/});
} else ...
};
...
This obviously works best if you want to handle all unauthorized requests the same. If you don't, however, the second option is to defer to the service that issued the request. For example, if you're using $http you can do this:
$http.get('/words/words/words/').then(function(){
// This is where you handle a successful request.
}, function(error) {
// Handle your error here. Please take note that this error message is
// whatever you sent back in the `reject` previously
});
Hopefully that clears a few things up.

Related

AngularJs http request priorities and http interceptors

I have been looking at an application I made a while back and there is a particular page where the details are being loaded last. Because of this, it seems to be queuing the request (there are more than 6 others before it) and that is causing the page to be slow.
I figured I could find a solution to prioritize these requests and I found this:
How to prioritize requests in angular $http service?
So I created my version of it and added it to my interceptors:
// Add our auth interceptor to handle authenticated requests
$httpProvider.interceptors.push('authInterceptor');
$httpProvider.interceptors.push('httpPriorityInterceptor');
The interceptor looks like this:
function factory($injector, $q) {
var requestStack = [], // request stack
$http = null; // http service to be lazy loaded
return {
request: request,
responseError: responseError
};
//////////////////////////////////////////////////
function request(config) {
// Lazy load $http service
if (!$http) {
$http = $injector.get('$http');
}
if (!config.hasBeenRequested) {
config.hasBeenRequested = true;
config.priority = config.priority || 3;
console.log(config);
// add a copy of the configuration
// to prevent it from copying the timeout property
requestStack.push(angular.copy(config));
// sort each configuration by priority
requestStack = requestStack.sort(sort);
// cancel request by adding a resolved promise
config.timeout = $q.when();
}
// return config
return config;
}
function responseError(rejection) {
// check if there are requests to be processed
if (requestStack.length > 0) {
requestStack.reduceRight(function(promise, config) {
return promise.finally(function() {
return $http(config);
});
}, $q.when());
requestStack.length = 0;
}
// return rejected request
return $q.reject(rejection);
}
//////////////////////////////////////////////////
function sort(config1, config2) {
return config1.priority < config2.priority;
}
}
The problem is, it seems to be intercepting template requests too. I have no issue with that, but they are not resolving. Instead I get a lot of errors:
Error: [$templateRequest:tpload] Failed to load template: app/accounts/accounts.html (HTTP status: -1 )
Has anyone encountered this before? Is there something I can do to fix this?
you should know that every request such as html files , css file and ... comes into interceptor.
in your case you dont need to prioritize this files. so you can filter your request like:
if (config.url.toString().toLowerCase().includes("api")) {
//place your functionality
}

How to save and set headers on each request with Angular Interceptors

I'm trying to setup token auth but can't seem to figure out a good way to save the headers the server is sending me into a cookie. I then need to read that value out of the cookie and into the new request headers.
I'm using Angular 1.5
Here's some simple code I've been trying to work with:
os.config(['$httpProvider', function ($httpProvider) {
return {
'request': function(config) {
var access_token = $cookies.get('access-token');
var token_type = $cookies.get('token-type');
var client = $cookies.get('client');
var uid = $cookies.get('uid');
$httpProvider.defaults.headers.post['access-token'] = access_token;
$httpProvider.defaults.headers.post['token-type'] = token_type;
$httpProvider.defaults.headers.post['client'] = client;
$httpProvider.defaults.headers.post['uid'] = uid;
},
'response': function(response) {
$cookies.put('access-token', 'oatmeal');
$cookies.put('token-type', 'oatmeal');
$cookies.put('client', 'oatmeal');
$cookies.put('uid', 'oatmeal');
}
};
}]);
EDIT: People have been commenting trying to show me how to set headers. I can do that easily, as you can see in the example. What I need to do is save the response headers after I've made a request. These need to be saved into a cookie which is what my question pertains to.
I'm interested in a method that doesn't require me to run some function in the .then part of every single API request I do, that's not DRY and seems like madness...

AngularJs watch for change in variable inside a service

I have a service named loginManager which stores objects called is_logged_in & api_token along with few others. My various controllers make ajax calls using $http using the api_token.
If the api_token is reset/expired on server, response is sent as auth_error, at this point I set is_logged_in = false
What i want to achieve is, whenever is_logged_in is changed, the service redirects to /login using $location.path('/login'), i.e. to say, I want to watch the object inside the service, and invoke callback on change from service itself.
I just want the service to take care of login and corresponding routing, without any controller worrying about weather user is logged in or not.
I believe Pankaj Pakar's answer could work but you should use angular's interceptors for that. They intercept all messages. You could add hook for response or responseError and when you recieve auth_error you do any action you like. For example $location.path('/login'), display error to user, etc.
If you want to separate logic you could inject your service with all code inside and just call some method on it.
I'd suggest you to put that watcher in run phase on the angular application which will be there at a single place, by which you could check the value is_logged_in flag of service & if user is not login then redirect him/her to login page directily.
Code
app.run(function($rootScope, loginManager, $location){
$rootScope.$watch(function(){
return loginManager.is_logged_in;
}, function(newValue){
if(angular.isDefine(newValue) && !newValue)
$location.path('/login');
//$state.go('login'); //if you are using ui.router
})
})
Edit
Really curious part of your question is, from where you are changing is_logged_in flag of your service as #JBNizet asked? If any code is there is JavaScript then you should directly redirect to login page from there.
I feel the need to answer something more, Mior is quite right, but his answer needs more meat.
Here I show you how I managed to handle ALL server XHR requests with response 401 unauthorized.
First of all you need a service:
'use strict';
angular.module('theModule')
.factory('interceptorService', ['$q', '$location', function ($q, $location) {
return {
response: function (response) {
return response || $q.when(response);
},
responseError: function (rejection) {
var returnTo = $location.path().replace(/^\/|\/$/g, '');
if (returnTo === 'login') {
return;
}
if (rejection.status === 401) {
console.log('Unauthorized');
$location.path('/login').search('returnTo', returnTo);
}
return $q.reject(rejection);
}
};
}]);
This will be used to intercept all XHR calls and to change the location every time a 401 error is found.
I've also added an improvement that is the "returnTo" parameter, you will be able to use it after login to return to the previous page.
To bind it to each request you have to call the config method, this is my main javascript.
'use strict';
/**
* #author Gianmarco Laggia
*
* Main module of the application an configurations.
*/
angular
.module('theModule', [])
.config(['$httpProvider', function ($httpProvider) {
//Http Interceptor to check auth failures for xhr requests
$httpProvider.interceptors.push('interceptorService');
}]);
This is pretty much what you need to intercept every request, working on the rejection.status you can also intercept events such as server down (status is -1), internal server error (500+), success status (in the response part, status 200+) etc.

Add data to angular $http request that will be returned in the response

I need to add some data to each response I send via $http in angular that will be in the response. In other works I'm trying to add an 'id' to the request because when the response is returned I need to associate it with the correct object that sent it. Is this possible? If so how would I go about it?
use interceptors, I'm quoting from the documentation:
For purposes of global error handling, authentication, or any kind of
synchronous or asynchronous pre-processing of request or
postprocessing of responses, it is desirable to be able to intercept
requests before they are handed to the server and responses before
they are handed over to the application code that initiated these
requests.
$provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
return {
'request': function(config) {
config.id = generateId(); //or a timestamp maybe?
return config;
},
'response': function(response) {
// do something on success
return response;
}
};
});
then add your
$httpProvider.interceptors.push('myHttpInterceptor');

AngularJS remote client Properties share to controllers/services

I want to develop an AngularJS web client for which the REST backend may be located on a different server.
So basically I am thinking of having a property input field on the frontend where for each session I will enter the base REST url (e.g., http://localhost:8080/backend/rest/)
Is there some sort of best practice to be able to share the base url amongst all controller/factories/services in order to include it for all $http requests?
I would configure an HTTP request interceptor service that would simply prepend the value to the URL passed to the $http service. Something like the following (not tested):
// register the interceptor as a service
$provide.factory('pathPrependerInterceptor', function() {
var _path = 'http://localhost:8080/backend/rest'; // default value
return {
request: function(config) {
config.url = _path + config.url
return config;
},
setPath: function(path) {
_path = path;
}
}
});
$httpProvider.interceptors.push('pathPrependerInterceptor');
I think the bast way would be to acomplish this with the use of interceptors. You can read more about this topic in the AngularJs documentation.
The idea is to set up an interceptor that will preappend the base url to each request something like this:
var app = angular.module('MyApp',[]);
app.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push(function($q) {
return {
'request': function(config) {
config.url = "http://api.openweathermap.org/data/2.5/" + config.url
return config;
}
};
});
}]);
I hope you got an idea.
There is JSFiddle that incorporates this idea.
The other answers are from a more experienced bunch, so take this with a pinch of salt, but they seem like overkill to me. You're configuring an application-wide variable which needs injection. Using
module.value("baseRestUrl", ...)
lets you inject baseRestUrl wherever it's required.

Resources