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?
Related
angular 1.6.1 has removed .success and .error in service $http for use .then and .catch.
Now my answer is:
It makes sense that I have written in my code? because now I'm confused on how the controller talks to the service
I have a controller for login (example):
LoginCTRL
User.login(data).then(
function(response){
//do something
},function(err){
//do somethig
});
And I have User service:
myapp.service('User', function User($q, $http) {
return {
login: function(credentials){
return $http.post('login',{data:data}).then(function onSuccess(response) {
return response;
}).catch(function onError(response){
return response;
});
},
}
It's correct write service.method().then in controller, now that there is no more $q.defer().resolve(data) or $q.defer().reject(reason)
try:
myapp.service('User', function User($q, $http) {
return {
login: function(credentials) {
var deferred = $q.defer();
$http.post('login', {
data: data
}).then(onSuccess, onFailure);
function onSuccess(response) {
deferred.resolve(response);
}
function onFailure(response) {
deferred.reject(response);
}
return deferred.promise;
}
}
}
I'm trying to add a popup with retry option as follows so that the user can click on that when lose the connection during a HTTP call.
angular.module('app.services', [])
.factory('httpResponseErrorInterceptor', ['$injector', '$q', function($injector, $q) {
return {
'responseError': function(response) {
var $ionicPopup = $injector.get('$ionicPopup');
if (response.status === 0) {
var confirmPopup = $ionicPopup.confirm({
title: 'No Connectivity!',
template: 'Internet not available'
});
confirmPopup.then(function(res) {
if(res) {
var $http = $injector.get('$http');
return $http(response.config);
} else {
return $q.reject(response);
}
});
}
}
};
}])
It is receiving the response from the http call, but not returning the response to the calling point. In the same way I tried the following code,
.factory('httpResponseErrorInterceptor', ['$injector', '$q', function($injector, $q) {
return {
'responseError': function(response) {
if (response.status === 0) {
var $http = $injector.get('$http');
return $http(response.config);
}
return $q.reject(response);
}
};
}])
But this one is returning the response properly to the calling point when we get the connection back. I'm not sure where I'm going wrong in the first code.
Any help/idea would be appreciated.
You should return confirmPopup.then() call.
Do like this:
return confirmPopup.then(function(res) {
if(res) {
var $http = $injector.get('$http');
return $http(response.config);
} else {
return $q.reject(response);
}
});
Example of chaining:
var promise = confirmPopup.then(function(res) {
if(res) {
var $http = $injector.get('$http');
return $http(response.config);
} else {
return $q.reject(response);
}
});
promise.then(function(success){
//HTTP SUCCESS
}, function(error){
//HTTP ERROR OR REJECT RESPONSE
});
Based on Patrick Kelleter's answer, I framed this working solution,
.factory('httpResponseErrorInterceptor', ['$injector', '$q', function($injector, $q) {
return {
'responseError': function(response) {
var $ionicPopup = $injector.get('$ionicPopup');
var $ionicLoading = $injector.get('$ionicLoading');
$ionicLoading.hide();
if (response.status === 0) {
var userInputDefer = $q.defer();
var confirmPopup = $ionicPopup.confirm({
title: 'No Connectivity!',
template: 'Internet not available',
okText: 'Retry'
});
confirmPopup.then(function(res) {
if(res) {
var $http = $injector.get('$http');
userInputDefer.resolve($http(response.config));
} else {
userInputDefer.reject($q.reject(response));
}
});
return userInputDefer.promise;
}
}
};
}]);
Edit:
Just for future reference for someone, for using the above HTTP interceptor, you have to include the factory in config as follows,
.config(['$httpProvider',function($httpProvider) {
$httpProvider.interceptors.push('httpResponseErrorInterceptor');
}]);
confirmPopup.then(function(res) {
if(res) {
var $http = $injector.get('$http');
return $http(response.config);
} else {
return $q.reject(response);
}
});
here is the problem. you are returning stuff ($http / $q) in the asynchronous callback of the confirmPopup.
the confirmPopup is async and you define a callback via ".then".
whatever you are returning there will not reach your calling point. it is the return value of the callback. which probably will not land anywhere (depending on the implementation of confirmPopup, but i doubt that it expects you to return anything there)
you will have to use your own promise and return it synchronously at the end of your callback
I'm building my (first) angular app that will have tokens inserted into headers (the content shown is for the most part taken from here)
angular.module('myApp')
.factory('sessionInjector', ['SessionService', function(SessionService) {
var sessionInjector = {
request: function(config) {
config.headers['x-session-token'] = SessionService.getToken();
return config;
}
};
return sessionInjector;
}])
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('sessionInjector');
}])
The trouble I'm having is with SessionService - how can I initialize this with call to the server?
For example, this didn't work:
.factory('SessionService', ['$injector', function($injector){
var token = "";
return {
getToken: function () {
var http = $injector.get('$http');
if (token === "") {
http.get('http://localhost/api/auth/getToken').success(function (ret) {
token = ret;
});
}
return token;
//I can see a $q/deferred/promise should be used somehow here...
//but I'm not sure it solves the problem I'm having...
}
}
}]);
because it just overloads my cpu to 100%...
Since it's my first angular app, I'm sure I'm missing something, but... what?
EDIT:
Another take on the matter... still doesn't work though... (again, uses up cpu, probably infinite loop)
.factory('sessionData', function () {
var currentToken = '[uninitialized-token]';
return {
getToken: function () {
return currentToken;
},
setToken: function (token) {
currentToken = token;
}
}
})
.factory('sessionInjector', ['sessionData', '$injector', '$q', function (sessionData, $injector, $q) {
var sessionInjector = {
request: function (config) {
var deferred = $q.defer();
var http = $injector.get('$http');
http.get('http://localhost/api/auth/getToken').success(function (ret) {
sessionData.setToken(ret);
console.log("successfully authenticated with token " + sessionData.getToken());
config.headers['x-header-sessionID'] = sessionData.getToken();
deferred.resolve(config);
})
.error(function(){
console.log("failed to authenticate");
deferred.resolve(config);
});
return deferred.promise;
}
};
return sessionInjector;
}])
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push('sessionInjector');
}])
.run(['$http', 'sessionData', function ($http, configs, sessionData) {
$http.get('http://localhost/api/auth/testMethod').then(function (ret) {
//do something...
});
}])
Check whether this modified code fragment will solve your issues.
.factory('SessionService', ['$http', '$q', function($http, $q) {
var token = null;
var sessionService = {};
var differred = $q.defer();
sessionService.readToken = function() {
return $http.get('http://localhost/api/auth/getToken')
.success(function (res) {
console.log('Auth Success and token received: ' + JSON.stringify(res.data));
// Extract the token details from the received JSON object
token = res.data;
differred.resolve(res);
}, function (res) {
console.log('Error occurred : ' + JSON.stringify(res));
differred.reject(res);
}
)
};
sessionService.getToken = function() {
return token;
};
sessionService.isAnonymous = function() {
if (token)
return true;
else
return false;
};
return sessionService;
}])
.factory('sessionInjector', ['SessionService', function(SessionService) {
var sessionInjector = {
request: function(config) {
if (!sessionService.isAnonymous) {
config.headers['x-session-token'] = SessionService.getToken();
return config;
}
}
};
return sessionInjector;
}])
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('sessionInjector');
}])
Answer was actually pretty straight forward - if the targeted URL is for login, then don't inject anything (look for the comment the fix):
.factory('sessionData', function () {
var currentToken = '[uninitialized-token]';
return {
getToken: function () {
return currentToken;
},
setToken: function (token) {
currentToken = token;
}
}
})
.factory('sessionInjector', ['sessionData', '$injector', '$q', function (sessionData, $injector, $q) {
var sessionInjector = {
request: function (config) {
//The fix:
if(config.url === 'http://localhost/api/auth/getToken')
return config;
var deferred = $q.defer();
var http = $injector.get('$http');
http.get('http://localhost/api/auth/getToken').success(function (ret) {
sessionData.setToken(ret);
console.log("successfully authenticated with token " + sessionData.getToken());
config.headers['x-header-sessionID'] = sessionData.getToken();
deferred.resolve(config);
})
.error(function(){
console.log("failed to authenticate");
deferred.resolve(config);
});
return deferred.promise;
}
};
return sessionInjector;
}])
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push('sessionInjector');
}])
.run(['$http', 'sessionData', function ($http, configs, sessionData) {
$http.get('http://localhost/api/auth/testMethod').then(function (ret) {
//do something...
});
}])
I have the following code:
!(function(window, angular){
'use strict';
angular.module('interceptor', ['settings']).
config(function($httpProvider, $injector){
var $http,
interceptor = ['$q', '$injector', function ($q, $injector) {
var error;
function success(response) {
var $http = $http || $injector.get('$http');
var $timeout = $timeout || $injector.get('$timeout');
var $rootScope = $rootScope || $injector.get('$rootScope');
var LOADER_CONF = LOADER_CONF || $injector.get('LOADER_CONF');
if($http.pendingRequests.length < 1) {
$timeout(function(){
if($http.pendingRequests.length < 1){
$rootScope._loading = false;
}
}, LOADER_CONF.SUSPEND_ON);
}
else{
$timeout(function(){
if($http.pendingRequests.length > 0){
$rootScope._loading = true;
}
}, LOADER_CONF.SUSPEND_OFF);
}
return response;
}
function error(response) {
var $state = $state || $injector.get("$state");
var $timeout = $timeout || $injector.get('$timeout');
var $rootScope = $rootScope || $injector.get('$rootScope');
var LOADER_CONF = LOADER_CONF || $injector.get('LOADER_CONF');
$timeout(function(){ $rootScope._loading = false, LOADER_CONF.SUSPEND_OFF});
return $q.reject(response);
}
return function (promise) {
return promise.then(success, error);
}
}];
$httpProvider.responseInterceptors.push(interceptor);
});
})(window, window.angular);
a basic interceptor to toggle a spinner.
The interceptor works just fine for http requests that fetch partials, and internal angular stuff, but does not work for RestAngular requests (which is a wrapper for $http).
Any help is much appreciated.
it's possible Restangular interception isn't working because of promises. try using $q like so:
var interceptor = ['$rootScope', '$q', function ($rootScope, $q) {
return {
'request': function(config) {
console.log("request successful");
return config || $q.when(config);
},
'requestError': function(rejection) {
console.log("request error");
return $q.reject(rejection);
},
'response': function(response) {
console.log("response successful");
return response || $q.when(response);
},
'responseError' : function(rejection) {
console.log(rejection.status+" something bad happened");
$rootScope.errors = ["Unable to connect to server "+rejection.config.url];
return $q.reject(rejection);
}
};
}];
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")
}
}
}
}
]);