Using AngularJS for my application and for http post the server needs a token that we can get by http get. But I want to run the token_generate() function before each http call, because sometimes the token expires
token = function() {
var api = AuthService.getToken();
api.success(function (response) {
var token = response.token;
$http.defaults.headers.common['X-CSRF-TOKEN'] = token;
});
};
token();
You need to register an $http interceptor:
(function(window, angular) {
function tokenizerConfig($httpProvider) {
function registerInterceptors($q, $http, AuthenticationService) {
var interceptors = {};
interceptors.request = function(configs) {
if(AuthenticationService.isTokenValid) {
return $q.when(configs);
}
var qs = {};
return $http
.get(TOKEN_API, { cache: false, params: qs})
.then(function(result) {
AuthenticationService.setToken(result.data.token);
return configs;
})
;
};
return interceptors;
}
$httpProvider.interceptors.push(['$q', '$http', 'AuthenticationService', registerInterceptors]);
}
angular
.module('tokenizer', [])
.config(['$httpProvider', tokenizerConfig])
})(window, window.angular);
Related
I use cordovaFileTransfer for image upload in my ionic app. Also i use ng-token-auth for authorization. It exchanging tokens with devise through headers.
Problem is that ng-token-auth intercepting http service where tokens are updated, however cordova does not use http service.
So how is possible to handle reponse after file upload to update tokens that are in headers.
Updated.
Here i call file upload.
#cordovaFileTransfer.upload(
upload_path,
file_uri,
httpMethod: method,
headers: #auth.retrieveData('auth_headers')
)
Here is ng-token-auth code which intercepts http:
$httpProvider.interceptors.push([
'$injector', function($injector) {
return {
request: function(req) {
$injector.invoke([
'$http', '$auth', function($http, $auth) {
var key, val, _ref, _results;
if (req.url.match($auth.apiUrl())) {
_ref = $auth.retrieveData('auth_headers');
_results = [];
for (key in _ref) {
val = _ref[key];
_results.push(req.headers[key] = val);
}
console.log("REQUEST_NEW_HEADERS")
console.log(req)
console.log($auth.retrieveData('auth_headers'))
console.log(_results)
return _results;
}
}
]);
return req;
},
response: function(resp) {
$injector.invoke([
'$http', '$auth', function($http, $auth) {
if (resp.config.url.match($auth.apiUrl())) {
console.log('[RESP_SUCCESS]')
console.log(resp.headers())
return updateHeadersFromResponse($auth, resp);
}
}
]);
return resp;
},
responseError: function(resp) {
$injector.invoke([
'$http', '$auth', function($http, $auth) {
if (resp.config.url.match($auth.apiUrl())) {
console.log('[RESP_ERROR]')
console.log(resp.headers())
return updateHeadersFromResponse($auth, resp);
}
}
]);
return $injector.get('$q').reject(resp);
}
};
}
]);
I am new to AngularJS. What I want is getting a token coming from a server with $http post and then use that token coming from the request to use as an authorization header for access in other page and following data requests to the server. Here is my existing code:
var peopleApp = angular.module('peopleApp', ['ngRoute', 'ngAnimate']);
peopleApp.config(function($interpolateProvider, $httpProvider) {
// Change template tags
$interpolateProvider.startSymbol('[[');
$interpolateProvider.endSymbol(']]');
// Enabling CORS
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
// $httpProvider.defaults.withCredentials = true;
});
peopleApp.controller('formController', function($scope, $http, $location) {
$scope.logIn = function(json, url){
$http.post(url, json)
.then(function(response){
token = response.data.token;
window.location.href = 'crew-menu';
},function(response){
alert('Please check the following validations on the next alert and contact the creators regarding this error');
alert(JSON.stringify(response.data.errors));
});
}
});
P.S.
I am aware that this can be done by using the .run like this:
peopleApp.run(function($http) {
$http.defaults.headers.common.Authorization = 'YmVlcDpib29w';
});
However the token Authorization will be coming from a login authentication via post request
Step 1
Take the token from login response and save it somewhere in the app, most common solution is to store it in local storage so it will be available after browser restart.
$scope.logIn = function(json, url){
$http.post(url, json)
.then(function(response){
localStorageService.set('authorizationData', { token: response.data.token });
window.location.href = 'crew-menu';
},function(response){
alert('Please check the following validations on the next alert and contact the creators regarding this error');
alert(JSON.stringify(response.data.errors));
});
}
Step 2
Use angularjs $http interceptor to automatically add authentication header to every http request:
app.factory('authInterceptorService', ['$q', '$location', 'localStorageService', function ($q, $location, localStorageService) {
var authInterceptorServiceFactory = {};
var _request = function (config) {
config.headers = config.headers || {};
var authData = localStorageService.get('authorizationData');
if (authData) {
config.headers.Authorization = 'Bearer ' + authData.token;
}
return config;
}
var _responseError = function (rejection) {
if (rejection.status === 401) {
$location.path('/login');
}
return $q.reject(rejection);
}
authInterceptorServiceFactory.request = _request;
authInterceptorServiceFactory.responseError = _responseError;
return authInterceptorServiceFactory;
}]);
Or put it manualy every time you make http request:
function buildConfig() {
var c = {};
var authData = localStorageService.get('authorizationData');
if (authData) {
c.headers.Authorization = 'Bearer ' + authData.token;
}
return c;
}
function post(url, model) {
return $http.post(url, model, buildConfig());
}
More info: here and
my angular webapi project
Already solved it. The solution is to store the token in a localstorage first then use run function for it be a default. Here is the code:
var peopleApp = angular.module('peopleApp', ['ngRoute', 'ngAnimate']);
peopleApp.config(function($interpolateProvider, $httpProvider) {
// Change template tags
$interpolateProvider.startSymbol('[[');
$interpolateProvider.endSymbol(']]');
// Enabling CORS
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
// $httpProvider.defaults.withCredentials = true;
});
peopleApp.controller('formController', function($scope, $http, $location, $window) {
$scope.logIn = function(json, url){
$http.post(url, json)
.then(function(response){
token = response.data.token;
$window.localStorage.token = token;
window.location.href = 'crew-menu';
},function(response){
alert('Please check the following validations on the next alert and contact the creators regarding this error');
alert(JSON.stringify(response.data.errors));
});
}
});
peopleApp.run(function($window, $http){
if ($window.localStorage.token){
$http.defaults.headers.common.Authorization = "Token "+$window.localStorage.token;
}
});
var app = angular.module('app');
// register the interceptor as a service
app.factory('myHttpInterceptor', function($q ) {
return {
'request': function(config) {
return config;
}
};
});
I am trying to modify the urls of api calls and append the api url to the start of the ajax calls in the interceptor insted of each service function like
function getAssesmentResults(params) {
return $http.get(url.api + '/results', {params: params})
.then(function(response) {
return response.data;
});
}
However the interceptor intercepts all http requests like .css or .html or .json files. What is a good way to modify the urls in the interceptor without modifying other http requests?
$http has a facility to intercept and rewrite URLs that is configured on the $httpProvider. When I run in production, I have an extra token: '/rest/' compared with development mode and I detect production mode ( prefix packing ) in the interceptor. This is in my app.js
var rest_srvr = 'http://dev-pc.example.com:8000'
app.factory('REST_Interceptor',[
'$location',
function($location) {
var request = function(config) {
if (RegExp('packing','i').test(window.location.host)) {
return config
}
var rest_request_regex = new RegExp('^.*?/rest/(.*)$')
//console.log('config.url=%s',config.url)
config.url = config.url.replace(rest_request_regex,rest_srvr+'/$1')
var files_request_regex = new RegExp('^/(files/(.*))$')
config.url = config.url.replace(files_request_regex,rest_srvr+'/$1')
//console.log(' is now config.url=%s',config.url)
return config
}
var translate_subpath = function(subpath) {
return request({url:'https://'+$location.host()+subpath}).url
}
return {
request: request,
translate_subpath: translate_subpath
}
}])
app.config([
'$httpProvider','$cookiesProvider',
function($httpProvider, $cookiesProvider) {
if (!RegExp('packing','i').test(window.location.host)) {
$httpProvider.interceptors.push('REST_Interceptor')
}
}])
I would create a service that wraps the $http. Then in your code you'll always call this wrap instead of $http and will be able to do whatever you want with the request before it is sent. Just a simple example:
module.factory('myHttp', function($http){
return {
get: function(url, params){
var newUrl = "base-api-url/" + url;
return $http.get(newUrl, params);
}
}
})
Use the generic service for this:
Generic service
appCless.factory("$comum", function($http, $q, $injector) {
function ajax(url, parametros, metodo) {
var requisicao = $http({
method: metodo,
url: url,
data:parametros
});
var promessa = requisicao.then(
function(resposta) {
return(resposta.data);
},
function(resposta) {
return($q.reject("Something went wrong"));
}
);
return promessa;
}
return({
ajax:ajax
});
});
Service
app.factory("$categoriaproduto", function($comum) {
var categoria;
return {
buscar : function(neoId) {
var promessa = $comum.ajax("/fusion/services/roi/category/product/search", "", "POST");
promessa.then(function(req) {
categoria = req.data;
});
return promessa;
},
cache : function() {
return categoria;
}
};
});
I'm writing an angularjs client for a token based restful API. The tokens in the API expire every hour so every time the token is expired in my client there should be a refresh token action.
The controller which handles the API call results looks like this:
angular.module('apitestApp')
.controller('MainCtrl', ['$rootScope', '$scope', 'httpService', function ($rootScope, $scope, httpService) {
$scope.messages = [];
var url = $rootScope.domainPath + $rootScope.apiPath + 'messages.json';
httpService.getRequest(url, {}).then(
function (data){
$scope.messages = data;
}
);
}]);
I have a service that makes the API calls using angularjs $resource
angular.module('apitestApp')
.service('httpService', ['$rootScope', '$resource', '$localStorage', function ($rootScope, $resource, $localStorage) {
this.getRequest = function (url, params){
var res = $resource(url, params, {
query: {
method: 'GET',
isArray: true,
headers: { 'Authorization': 'Bearer ' + $localStorage.token.access_token }
}
});
return res.query().$promise;
};
this.refreshToken = function (){
var url = $rootScope.domainPath + this.authPath;
var request = $resource(url);
return request.get({
client_id: this.clientId,
client_secret: this.secret,
grant_type: 'refresh_token',
refresh_token: $localStorage.token.refresh_token
},
function (data){
$localStorage.token = data;
}
).$promise;
};
}]);
And finally an interceptor that handles all unauthorized requests (401), refresh the access token and retries the failed request.
angular.module('apitestApp')
.factory('apiInterceptor', ['$q', '$injector', function ($q, $injector){
//Handling error codes
return {
response : function (response){
return response || $q.when(response);
},
responseError: function (rejection){
switch(rejection.status){
case 400:
console.log("Bad request");
break;
case 401:
var config = rejection.config;
var deferred = $q.defer();
var httpService = $injector.get('httpService');
httpService.refreshToken().then(deferred.resolve, deferred.reject);
return deferred.promise.then(function (){
return httpService.getRequest(config.url, config.params);
});
//break;
case 500:
console.log("Internal server error");
break;
default:
console.log("Another error");
break;
}
return $q.reject(rejection);
}
};
}]);
When the access token is valid, getRequest() method in my service successfully returns a promise, this is the same I want the interceptor to return but is not. In case the access token has expired the interceptor catches a 401 error, then updates the access token and finally makes the same request, the problem is that my controller doesn't get any response of it.
How can I perform a refresh token action and return the expected data on the behalf of the user? What am I doing wrong in the interceptor?
You're going to want to remove the $rootScope provider from the controller, that is not best practices for Angular as the controller has it's own scope inside of $rootScope. Services and Factories are okay to put on the $rootScope as it does not create it's own scope and that is where they will listen for their own events.
Also, it's best practice to put any asynchronous activity/HTTP calls into the services/factories. Just remember "skinny controllers, fat services".
Maybe try using an async handler that uses a sort of publish/subscribe design. Now, if it fails, it will call to update the stored value of messages once the getRequest function has completed async, triggering an update to the scope digest for any controller subscribed to the method:
Controller
angular.module('apitestApp')
.controller('MainCtrl', ['$scope', 'httpService', function ($scope, httpService) {
$scope.messages = [];
httpService.setPath();
httpService.onMessageReady($scope, function (messagesData) {
$scope.messages = messagesData;
});
}]);
Service
angular.module('apitestApp')
.service('httpService', ['$rootScope', '$resource', '$localStorage', function ($rootScope, $resource, $localStorage) {
var self = this;
this.messages = undefined;
this.setPath = function () {
self.getRequest($rootScope.domainPath + $rootScope.apiPath + 'messages.json', {});
};
this.getRequest = function (url, params) {
var res = $resource(url, params, {
query: {
method: 'GET',
isArray: true,
headers: { 'Authorization': 'Bearer ' + $localStorage.token.access_token }
}
});
return res.query().$promise.then(function (data) {
if (data) {
self.messages = data;
$rootScope.$broadcast('messagesReady');
}
});
};
this.refreshToken = function (){
var url = $rootScope.domainPath + this.authPath;
var request = $resource(url);
return request.get({
client_id: this.clientId,
client_secret: this.secret,
grant_type: 'refresh_token',
refresh_token: $localStorage.token.refresh_token
},
function (data){
$localStorage.token = data;
}
).$promise;
};
this.onMessageReady = function (scope, callback) {
callback(this.messages);
scope.$on('messagesReady', function () {
callback(this.messages);
});
};
}]);
var app = angular.module('app', ['ngResource']);
app.factory('UserFactory', function ($resource) {
return $resource('/com/vsoft/rest/users', {}, {
query: {
method: 'GET',
params: {},
isArray: false
}
});
});
app.controller('MyCtrl1', ['$scope', 'UserFactory', function ($scope, UserFactory) {
UserFactory.get({}, function (userFactory) {
$scope.firstname = userFactory.firstName;
$scope.lastname = userFactory.lastName;
});
});
}]);
i added above app in my html.But the app and angular-resource.js but my app.js is not exeuting.
If i removed ngResource module and $resource alert is coming.But if i used ngResource im nt getting alert.
Please help in this.If any one knows any Good Example to use Restful services with angularjs .Please Kindly send Url or code.
Please help me.
i called{{firstname}}
in my html but its not coming .
I use a service for handling RESTful messages
app.service('restService', function ($http, $log) {
'use strict';
var self = this;
var BASE_URL = "base/url/";
//First way how to do it
self.httpGet = function (url) {
$log.info("HTTP Get", url);
return postProcess($http({method: 'GET', url: BASE_URL + url}));
};
//Second way how to do it
self.httpPut = function (url, object) {
$log.info("HTTP Put", url);
return postProcess($http.put(BASE_URL + url, object));
};
self.httpPost = function (url, object) {
$log.info("HTTP Post", url);
return postProcess($http.post(BASE_URL + url, object));
};
self.httpDelete = function (url) {
$log.info("HTTP Delete", url);
return postProcess($http.delete(BASE_URL + url));
};
function postProcess(httpPromise) {
return httpPromise.then(function (response) {
if (response.status === 200) {
return response;
}
//Other than 200 is not ok (this is application specific)
failure(response);
}, function (response) {
failure(response);
});
}
/**
* Promise for failure HTTP codes
* #param response the HTTP response
*/
function failure(response) {
//Error handling
}
});
usable as
restService.httpGet("categories").then(function (response) {
categoryData = angular.fromJson(response.data);
//Broadcast an event to tell that the data is ready to be used
$rootScope.$broadcast("categoriesReady");
});