Add header to all requests in AngularJS application created by Yeoman - javascript

I've just built my very first AngularJS application using Yeoman. I've done it like so:
$ yo angular frontend
As a result, I've got a bunch of standard folders and files like:
- app
- images
- scripts
app.js
- styles
- views
index.html
...
- bower_components
- node_modules
- test
It seems like I have to change app.js file in order to add a header to all requests. But I'm terribly new to AngularJs and I do not know what should I exactly do. Now, app.js looks like:
angular
.module('frontend', [
...
])
.config(function($routeProvider){
$routeProvider
.when(...)
});
I guess, I need to set $httpProvider, but how can I do that?

You should use an interceptor for this. Here is the recommended way from the AngularJS docs:
// 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');
All you need to do is implement the 'request' method, as all methods are optional. The config object provided is an angular $http config object, and it contains a headers property. You should be able to add your header easily enough to this:
config.headers.myHeader = myValue;
return config;
You can grab the $httpProvider in your config blog simply by adding it to the arguments list:
angular
.module('frontend', [
...
])
.config(function($routeProvider, $httpProvider, $provide){
$routeProvider
.when(...)
// register the interceptor as a service
$provide.factory('myHttpInterceptor', function() {
return {
// optional method
'request': function(config) {
config.headers.myHeader = myValue;
return config;
},
};
});
$httpProvider.interceptors.push('myHttpInterceptor');
});

A better solution to add headers in all request is
app.run(['$http', function ($http) {
$http.defaults.headers.common['myHeader'] = 'myHeaderValue';
}]);

Related

Circular dependency found in angular js

Tring to add interceptor header for my every request, however, it is giving me below error.
Uncaught Error: [$injector:cdep] Circular dependency found: $http <- Auth <- httpRequestInterceptor <- $http <- $templateRequest <- $route
app.js
var app= angular.module('myDemoApp',['ngRoute'])
app.factory('httpRequestInterceptor', ['Auth', function (Auth) {
return {
request: function (config) {
config.headers['x-access-token'] = Auth.getToken();
return config;
}
};
}]);
app.config(function ($httpProvider) {
$httpProvider.interceptors.push('httpRequestInterceptor');
});
Auth Service
(function () {
'use strict';
myDemoApp.factory('Auth', ['$http', '$window', Auth]);
/******Auth function start*****/
function Auth($http, $window) {
var authFactory = {};
authFactory.setToken = setToken;
authFactory.getToken = getToken;
return authFactory;
/*setToken function start*/
function setToken(token) {
if (token) {
$window.localStorage.setItem('token', token);
} else {
$window.localStorage.removeItem('token');
}
}
/*getToken function start*/
function getToken() {
return $window.localStorage.getItem('token')
}
}
})();
You can't do this because.
You have created httpRequestInterceptor which intercepts all $http requests.
Now, you are passing Auth in the httpRequestInterceptor.
If you'll see, Auth uses $http request inside itself.
So, your interceptor can itself cause a http request using Auth.
Hence, its circular error and angularjs wont allow you to do that !
Remove $http from Auth factory OR dont insert a service into interceptor which itself uses $http.
I hope you got, how the infinite loop chain being created, hence a circular dependency error !

How use inside common provider $http in Angular 1.4?

I try to use common provider and in angular app config function set data in provider.
.provider('userData', function() {
var authUser = {};
return {
checkUser: function() {
// wish to able use $http here for request to get data from service
// save all data into 'authUser' object
// $get return 'authUser' object
},
getCookie: function(value) {
// wish to able use $http here
},
$get: function() {
// only for return object
return authUser;
}
}
})
app.config(['userDataProvider', function(userDataProvider) {
userDataProvider.checkUser();
});
.controller('headerCtrl', ['$scope', 'userData', '$http', function($scope, userData, $http) {
// use inside all controllers/directives 'userData'
});
I try to use $http as parameter in $get function -> not working: error:
$get: function($http) {
return authUser;
}
Also I can't find any valid example for using $http inside provider. Inside service/factory $http work fine, but I need to prepare data in provider from config function.
Services, factory & value aren't available at config phase by design.
You could do it in run()
app.run(['userData', function(userData) {
userData.checkUser();
});

Prevent route from loading until data is available in Angular

In the app I'm working on there is a situation in which data is pulled from a JSON file and is referenced in all subsequent routes. I want to ensure that a route does not load until this data is available, and if not available request it before loading the route. I'm trying to use a route resolve to accomplish this but am finding that the route will load regardless since the request to get the data returns a promise in the resolve. Here's an example of how the code for this is set up, is there a way to not load the route until the promise is resolved? I think the use of promises are throwing me off some.
Factory which pulls the data from the JSON file.
.factory('jsonFactory', function($q, $http) {
return {
getFormStuff: function() {
var deferred = $q.defer(),
httpPromise = $http.get('json/MasterObject.json');
httpPromise.then(function(response) {
deferred.resolve(response);
}, function(error) {
console.log(error);
});
return deferred.promise;
}
};
})
ngRoute config with resolve that checks if Model.formStuff is available and if not attempts to get it before loading the route which needs the data.
app.config(function ($routeProvider) {
$routeProvider.when('/someRoute', {
controller: 'someController',
templateUrl: 'views/someView.html',
resolve: {
getFormTemplate: function (Model, jsonFactory) {
if (!Model.formStuff) {
Model.formStuff = jsonFactory.getFormStuff();
return Model.formStuff;
} else {
return Model.formStuff;
}
}
}
})
EDIT: Adding the Model factory and controller where Model.formStuff is referenced. The Model.formStuff is dynamically added in a different controller and is not a pre-defined property...I inherited this code so not sure why it is handled like that.
angular.module('example', [])
.factory('Model', ['$resource',
function($resource) {
return {
query: function() {
return data;
},
get: function(id) {
return findById(id);
},
set: function(item) {
addItem(item);
},
put: function(item) {
updateItem(item);
},
del: function(id) {
removeItem(id);
},
getLoginUser: function(id) {
removeItem(id);
},
islogin: false
};
}
])
basic controller example showing how Model.formStuff is normally used.
angular.module(...)
.controller("someController", function(Model) {
$scope.someField = Model.formStuff[0].someProp;
var someVar = Model.formStuff.[0].otherProp;
// bunch of other code...
});
The code doesn't look that wrong. Please be sure to also handle the error case, otherwise the promise you return will never be rejected and the router will wait forever in case of some error. So you should call deferred.reject(error) in your error callback.
If you don't need any special processing on the data, you could directly return the promise of the $http.get() call like so:
getFormStuff = function() {
return $http.get('json/MasterObject.json');
}
What could possibly be the problem in your case is the controller definition. Do you inject a value named like the key in your resolve object into the controller? Otherwise the data will not be passed there...
angular.module(...)
.controller("someController", function(getFormTemplate) {
// do anything with the resolved data...
});
Another suggestion: Instead of handling the caching stuff directly in the resolve block, why not creating a special service that caches the data and just resolving like so:
resolve: {
getFormTemplate: function (MyCachingModel) {
return MyCachingModel.promiseToTemplate()
}
}
... and then moving the current logic into this caching service. This makes reasoning much clearer as your logic is not spread into the router config.

angular function dependency

Where do i place the common handleError and handleSuccess functions.
These are common functions that will be used by every service. where is the best place to put these functions. Should i put theses as global functions and inject them as dependency.
(function () {
"use strict";
angular.module('myApp.Group')
.service('GroupService', function ($http, $q, $location, Environment) {
// I transform the error response, unwrapping the application dta from
// the API response payload.
function handleError(response) {
// The API response from the server should be returned in a
// nomralized format. However, if the request was not handled by the
// server (or what not handles properly - ex. server error), then we
// may have to normalize it on our end, as best we can.
if (!angular.isObject(response.data) || !response.data.message) {
return ($q.reject("An unknown error occurred."));
}
// Otherwise, use expected error message.
return ($q.reject(response.data.message));
}
// I transform the successful response, unwrapping the application data
// from the API response payload.
function handleSuccess(response) {
return (response.data);
}
this.remove = function (id) {
var request = $http({
method: "delete",
url: '/group/' + id
});
return (request.then(handleSuccess, handleError));
};
});
}());
Here's how we do it:
We use the $httpProvider to intercept the responses and deal with each code on their own. We made a service to handle this functionality.
Our app config looks like this:
appModule.config(['$routeProvider', '$locationProvider', '$httpProvider', '$provide',
function ($routeProvider, $locationProvider, $httpProvider, $provide) {
// Http interceptor to handle session timeouts and basic errors
$httpProvider.responseInterceptors.push(['httpHandlersSrv', function (httpHandlersSrv) {
return function (promise) { return promise.then(httpHandlersSrv.success, httpHandlersSrv.error); };
}]);
routeProvider = $routeProvider;
$locationProvider.html5Mode(true);
}
]);
This is what our $httpHandlersSrv looks like where we handle errors. Notice we just pass the successful responses along without doing anything:
angular.module('appModule').factory('httpHandlersSrv', ['$q', '$location', '$rootScope', 'toaster', '$window', function ($q, $location, $rootScope, toaster, $window) {
return {
success: function (response) {
return response;
},
error: function (response) {
switch (response.status) {
case 0:
//Do something when we don't get a response back
break;
case 401:
//Do something when we get an authorization error
break;
case 400:
//Do something for other errors
break;
case 500:
//Do something when we get a server error
break;
default:
//Do something with other error codes
break;
}
return $q.reject(response);
}
};
}]);

AngularJS: Injecting service into a HTTP interceptor (Circular dependency)

I'm trying to write a HTTP interceptor for my AngularJS app to handle authentication.
This code works, but I'm concerned about manually injecting a service since I thought Angular is supposed to handle this automatically:
app.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push(function ($location, $injector) {
return {
'request': function (config) {
//injected manually to get around circular dependency problem.
var AuthService = $injector.get('AuthService');
console.log(AuthService);
console.log('in request interceptor');
if (!AuthService.isAuthenticated() && $location.path != '/login') {
console.log('user is not logged in.');
$location.path('/login');
}
return config;
}
};
})
}]);
What I started out doing, but ran into circular dependency problems:
app.config(function ($provide, $httpProvider) {
$provide.factory('HttpInterceptor', function ($q, $location, AuthService) {
return {
'request': function (config) {
console.log('in request interceptor.');
if (!AuthService.isAuthenticated() && $location.path != '/login') {
console.log('user is not logged in.');
$location.path('/login');
}
return config;
}
};
});
$httpProvider.interceptors.push('HttpInterceptor');
});
Another reason why I'm concerned is that the section on $http in the Angular Docs seem to show a way to get dependencies injected the "regular way" into a Http interceptor. See their code snippet under "Interceptors":
// 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 || $q.when(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 || $q.when(response);
},
// optional method
'responseError': function(rejection) {
// do something on error
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
};
}
});
$httpProvider.interceptors.push('myHttpInterceptor');
Where should the above code go?
I guess my question is what's the right way to go about doing this?
Thanks, and I hope my question was clear enough.
This is what I ended up doing
.config(['$httpProvider', function ($httpProvider) {
//enable cors
$httpProvider.defaults.useXDomain = true;
$httpProvider.interceptors.push(['$location', '$injector', '$q', function ($location, $injector, $q) {
return {
'request': function (config) {
//injected manually to get around circular dependency problem.
var AuthService = $injector.get('Auth');
if (!AuthService.isAuthenticated()) {
$location.path('/login');
} else {
//add session_id as a bearer token in header of all outgoing HTTP requests.
var currentUser = AuthService.getCurrentUser();
if (currentUser !== null) {
var sessionId = AuthService.getCurrentUser().sessionId;
if (sessionId) {
config.headers.Authorization = 'Bearer ' + sessionId;
}
}
}
//add headers
return config;
},
'responseError': function (rejection) {
if (rejection.status === 401) {
//injected manually to get around circular dependency problem.
var AuthService = $injector.get('Auth');
//if server returns 401 despite user being authenticated on app side, it means session timed out on server
if (AuthService.isAuthenticated()) {
AuthService.appLogOut();
}
$location.path('/login');
return $q.reject(rejection);
}
}
};
}]);
}]);
Note: The $injector.get calls should be within the methods of the interceptor, if you try to use them elsewhere you will continue to get a circular dependency error in JS.
You have a circular dependency between $http and your AuthService.
What you are doing by using the $injector service is solving the chicken-and-egg problem by delaying the dependency of $http on the AuthService.
I believe that what you did is actually the simplest way of doing it.
You could also do this by:
Registering the interceptor later (doing so in a run() block instead of a config() block might already do the trick). But can you guarantee that $http hasn't been called already?
"Injecting" $http manually into the AuthService when you're registering the interceptor by calling AuthService.setHttp() or something.
...
I think using the $injector directly is an antipattern.
A way to break the circular dependency is to use an event:
Instead of injecting $state, inject $rootScope.
Instead of redirecting directly, do
this.$rootScope.$emit("unauthorized");
plus
angular
.module('foo')
.run(function($rootScope, $state) {
$rootScope.$on('unauthorized', () => {
$state.transitionTo('login');
});
});
Bad logic made such results
Actually there is no point of seeking is user authored or not in Http Interceptor. I would recomend to wrap your all HTTP requests into single .service (or .factory, or into .provider), and use it for ALL requests. On each time you call function, you can check is user logged in or not. If all is ok, allow send request.
In your case, Angular application will send request in any case, you just checking authorization there, and after that JavaScript will send request.
Core of your problem
myHttpInterceptor is called under $httpProvider instance. Your AuthService uses $http, or $resource, and here you have dependency recursion, or circular dependency. If your remove that dependency from AuthService, than you will not see that error.
Also as #Pieter Herroelen pointed, you could place this interceptor in your module module.run, but this will be more like a hack, not a solution.
If your up to do clean and self descriptive code, you must go with some of SOLID principles.
At least Single Responsibility principle will help you a lot in such situations.
If you're just checking for the Auth state (isAuthorized()) I would recommend to put that state in a separate module, say "Auth", which just holds the state and doesn't use $http itself.
app.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push(function ($location, Auth) {
return {
'request': function (config) {
if (!Auth.isAuthenticated() && $location.path != '/login') {
console.log('user is not logged in.');
$location.path('/login');
}
return config;
}
}
})
}])
Auth Module:
angular
.module('app')
.factory('Auth', Auth)
function Auth() {
var $scope = {}
$scope.sessionId = localStorage.getItem('sessionId')
$scope.authorized = $scope.sessionId !== null
//... other auth relevant data
$scope.isAuthorized = function() {
return $scope.authorized
}
return $scope
}
(i used localStorage to store the sessionId on client side here, but you can also set this inside your AuthService after a $http call for example)

Categories