ExpressJS is sending the following response...
res.send('ItemUploaded');
I'm trying to get AngularJS to see this response via an Interceptor and perform a redirect. Does anyone have sample code where Angular catches a server response (such as my "ItemUploaded") and performs a redirect to a partial (via $location)?
This works fine. I have used it in my application.
var interceptor = function ($q, $location) {
return {
request: function (config) {//req
console.log(config);
return config;
},
response: function (result) {//res
console.log('Repos:');
console.log(result.status);
return result;
},
responseError: function (rejection) {//error
console.log('Failed with', rejection.status, 'status');
if (rejection.status == 403) {
$location.url('/dashboard');
}
return $q.reject(rejection);
}
}
};
module.config(function ($httpProvider) {
$httpProvider.interceptors.push(interceptor);
});
Here is the factory for the interceptor:
.factory('InterceptorService',['$q', '$location', function( $q, $location, $http){
var InterceptorServiceFactory = {};
var _request = function(config){
//success logic here
return config;
}
var _responseError = function(rejection) {
//error here. for example server respond with 401
return $q.reject(rejection);
}
InterceptorServiceFactory.request = _request;
InterceptorServiceFactory.responseError = _responseError;
return InterceptorServiceFactory;
}]);
then register the interceptor:
.config(["$httpProvider", function ($httpProvider) {
$httpProvider.interceptors.push('InterceptorService');
}]);
Every request coming will be passed here.
You can implement a interceptor factory which will redirect if it gets a matching result.
angular
.module('app')
.factory("httpinterceptor", ["$location",
function(location) {
return {
'response': function(response) {
if (response.data === "ItemUploaded") {
location.path("/ItemUploaded")
}
}
}
}
]);
Related
My question is what is the best way to handle errors from http REST calls. Should I use interceptors or decorators? My rest functions look something like this:
queryFunction : function (config) {
var defer = $q.defer();
var config = {};
$http.get(someUrl, config) //or http.put, delete
.then(function (response) {
defer.resolve(response.data);
})
.catch(function (ex) {
defer.reject(ex);
});
return defer.promise;
},
How will the simplest interceptor look?
Here's the code for generic $http ErrorInterceptor:
app.factory('ErrorInterceptor', ['$q', function($q) {
return {
responseError: function(rejection) {
// An error message is shown for all kind of server errors
if (rejection.status == -1) {
//alert('Backend is not working');
//use rejection.data variable
}
return $q.reject(rejection);
}
};
}])
Then it can be included into app config
app.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('ErrorInterceptor');
})
I'm trying to configure the $http service of Angular, to redirect to an URL when the status code is 403.
No problems so far but the URL to redirect to is coming from the server, through a service which is using $http (obiously).
Here's a piece of code:
angular
.module('app')
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push(['$q', 'URLs',
function($q, Redirect) {
return {
request: function(config) {
return config || $q.when(config);
},
responseError: function(response) {
if(response.status === 403) {
// redirect to URLs.login
}
return $q.reject(response);
}
};
}
]);
}])
.factory('URLs', ['$http', function($http) {
var URLs;
$http.get('/urls').then(function(response) {
URLs = response.data;
});
return URLs;
}]);
This code is creating a circular dependency (error) in Angular.
Is there a way that I can do this, having dynamic URLs that are coming from a server and based on this to redirect the user to one of them when the response.status is 403?
Use $injector service to lazily load the URLs service:
angular
.module('app')
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push(['$q', '$injector',
function($q, $injector) {
return {
request: function(config) {
return config || $q.when(config);
},
responseError: function(response) {
var Redirect = $injector.get('URLs');
if(response.status === 403) {
// redirect to URLs.login
}
return $q.reject(response);
}
};
}
]);
}])
You can also break this circular dependency in the URLs service by injecting the $injector there.
I'd like to implement authentication on a single page web app with Angular.js. The official Angular documentation recommends the using of interceptors:
$provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
return {
// ...
'responseError': function(rejection) {
// do something on error
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
}
};
});
The problem is when the server sends 401 error, the browser immediately stops with "Unauthorized" message, or with login pop-up window (when authentication HTTP header is sent by the server), but Angular can't capture with it's interceptor the HTTP error to handle, as recommended. Am I misunderstanding something? I tried more examples found on web (this, this and this for example), but none of them worked.
For AngularJS >1.3 use $httpProvider.interceptors.push('myHttpInterceptor');
.service('authInterceptor', function($q) {
var service = this;
service.responseError = function(response) {
if (response.status == 401){
window.location = "/login";
}
return $q.reject(response);
};
})
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('authInterceptor');
}])
in app config block:
var interceptor = ['$rootScope', '$q', "Base64", function(scope, $q, Base64) {
function success(response) {
return response;
}
function error(response) {
var status = response.status;
if (status == 401) {
//AuthFactory.clearUser();
window.location = "/account/login?redirectUrl=" + Base64.encode(document.URL);
return;
}
// otherwise
return $q.reject(response);
}
return function(promise) {
return promise.then(success, error);
}
}];
I don't know why, but response with 401 error goes into success function.
'responseError': function(rejection)
{
// do something on error
if (rejection.status == 401)
{
$rootScope.signOut();
}
return $q.reject(rejection);
},
'response': function (response) {
// do something on error
if (response.status == 401) {
$rootScope.signOut();
};
return response || $q.when(response);
}
AngularJS interceptors only work for calls made with the $http service; if you navigate to a page that returns a 401, AngularJS never even runs.
I am trying to call AuthenticationService.logout() on a 401 http error. However, I can't make it work. I suppose I can't inject a service to the config, but how can I achieve this then?
myApp.config(['$httpProvider', 'AuthenticationService', function ($httpProvider, AuthenticationService) {
var interceptor = ['$rootScope','$q', function(scope, $q) {
function success(response) {
return response;
}
function error(response) {
var status = response.status;
if (status == 401) {
var deferred = $q.defer();
var req = {
config: response.config,
deferred: deferred
}
/* LOGOUT HERE */
AuthenticationService.logout();
return deferred.promise;
}
return $q.reject(response);
}
return function(promise) {
return promise.then(success, error);
}
}];
$httpProvider.responseInterceptors.push(interceptor);
}]);
What am I doing wrong, and how can I fix it?
I am looking for a way to do these two things, first I want to redirect the user to a login page if no SessionID is found and second I would like to hear your opinion about persisting session ID in memory only (no cookies).
The solution I came up with for the redirect is:
1 - Create a service called OAuth that will check if SessionID exists and if not, redirects to login page, the service is also responsible for the login and logout methods.
app.factory('OAuth', ['$http', function ($http) {
var _SessionID = '';
return {
login: function () {
//Do login ans store sessionID in var _SessionID
},
logout: function () {
//Do logout
},
isLoggedIn: function () {
if(_SessionID) {
return true;
}
//redirect to login page if false
}
};
}]);
2 - Inject the new OAuth service in each controller and check if user isLoggedIn
app.controller('myCtrl', ['$scope', 'OAuth', function ($scope, OAuth) {
//check if user is logged
OAuth.isLoggedIn();
}]);
Questions:
1 - The isLoggedIn() method will be called in all controllers, so I wonder if there is a way to do this without having to inject the service and call it in each controller.
2 - Instead of having a cookie to store the sessionID I want to save it in OAuth's _SessionID variable and for each request send it to the server. Is this a viable/secure approach? Can you give me some ideas for that?
Thanks!
I use a similar strategy (intercepting 401 responses from the server). You can check out the full example here : https://github.com/Khelldar/Angular-Express-Train-Seed
It uses node and mobgodb on the backend for session store and a trimmed down http interceptor on the client that doens't retry requests like the one Dan linked above:
var interceptor = ['$q', '$location', '$rootScope', function ($q, $location, $rootScope) {
function success(response) {
return response;
}
function error(response) {
var status = response.status;
if (status == 401) {
$location.path('/login');
}
return $q.reject(response);
}
return function (promise) {
return promise.then(success, error);
}
}];
$httpProvider.responseInterceptors.push(interceptor);
I would start here, Witold has created this cool interceptor that works off of http responses. I use it and its been really helpful.
In my case, I used
interceptor with $httpProvider
config
and $window dependency, as $location just appended the path to the existing url. What happened was like "http://www.tnote.me/#/api/auth", and it should have bene like "http://www.tnote.me/auth"
The code snippet is like this.
noteApp = angular.module('noteApp', ['ngRoute', 'ngCookies'])
.factory('authInterceptor', ['$rootScope', '$q', '$cookies', '$window',
function($rootScope, $q, $cookies, $window) {
return {
request: function (req) {
req.headers = req.headers || {};
if ($cookies.token) {
req.headers.Authorization = 'Bearer ' + $cookies.token;
}
return req;
},
responseError: function (rejection) {
if (rejection.status == 401) {
$window.location = '/auth';
}
return $q.reject(rejection);
}
}
}])
.config(['$routeProvider', '$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('authInterceptor');
}
])
this will work. It works fine in my application
var interceptor = function ($q, $location) {
return {
request: function (config) {//req
console.log(config);
return config;
},
response: function (result) {//res
console.log('Repos:');
console.log(result.status);
return result;
},
responseError: function (rejection) {//error
console.log('Failed with', rejection.status, 'status');
if (rejection.status == 403) {
$location.url('/dashboard');
}
return $q.reject(rejection);
}
}
};
module.config(function ($httpProvider) {
$httpProvider.interceptors.push(interceptor);
});