I've been working on an AngularJS project which has to send AJAX calls to an restfull webservice. This webservice is on another domain so I had to enable cors on the server. I did this by setting these headers:
cresp.getHttpHeaders().putSingle("Access-Control-Allow-Origin", "http://localhost:8000");
cresp.getHttpHeaders().putSingle("Access-Control-Allow-Credentials", "true");
cresp.getHttpHeaders().putSingle("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
cresp.getHttpHeaders().putSingle("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");
I'm able to send AJAX requests from AngularJS to the backend but I'm facing a problem when I try to get an attribute of a session. I believe this is because the sessionid cookie doesn't get send to the backend.
I was able to fix this in jQuery by setting withCredentials to true.
$("#login").click(function() {
$.ajax({
url: "http://localhost:8080/api/login",
data : '{"identifier" : "admin", "password" : "admin"}',
contentType : 'application/json',
type : 'POST',
xhrFields: {
withCredentials: true
},
success: function(data) {
console.log(data);
},
error: function(data) {
console.log(data);
}
})
});
$("#check").click(function() {
$.ajax({
url: "http://localhost:8080/api/ping",
method: "GET",
xhrFields: {
withCredentials: true
},
success: function(data) {
console.log(data);
}
})
});
The problem that I'm facing is that I can't get this to work in AngularJS with the $http service. I tried it like this:
$http.post("http://localhost:8080/api/login", $scope.credentials, {withCredentials : true}).
success(function(data) {
$location.path('/');
console.log(data);
}).
error(function(data, error) {
console.log(error);
});
Can anyone tell me what I'm doing wrong?
You should pass a configuration object, like so
$http.post(url, {withCredentials: true, ...})
or in older versions:
$http({withCredentials: true, ...}).post(...)
See also your other question.
In your app config function add this :
$httpProvider.defaults.withCredentials = true;
It will append this header for all your requests.
Dont forget to inject $httpProvider
EDIT : 2015-07-29
Here is another solution :
HttpIntercepter can be used for adding common headers as well as common parameters.
Add this in your config :
$httpProvider.interceptors.push('UtimfHttpIntercepter');
and create factory with name UtimfHttpIntercepter
angular.module('utimf.services', [])
.factory('UtimfHttpIntercepter', UtimfHttpIntercepter)
UtimfHttpIntercepter.$inject = ['$q'];
function UtimfHttpIntercepter($q) {
var authFactory = {};
var _request = function (config) {
config.headers = config.headers || {}; // change/add hearders
config.data = config.data || {}; // change/add post data
config.params = config.params || {}; //change/add querystring params
return config || $q.when(config);
}
var _requestError = function (rejection) {
// handle if there is a request error
return $q.reject(rejection);
}
var _response = function(response){
// handle your response
return response || $q.when(response);
}
var _responseError = function (rejection) {
// handle if there is a request error
return $q.reject(rejection);
}
authFactory.request = _request;
authFactory.requestError = _requestError;
authFactory.response = _response;
authFactory.responseError = _responseError;
return authFactory;
}
Clarification:
$http.post(url, {withCredentials: true, ...})
should be
$http.post(url, data, {withCredentials: true, ...})
as per https://docs.angularjs.org/api/ng/service/$http
Related
I am working on HTTP post request in angular JS (1.5).
First I pass request data to factory method. Call the Http post request and send the response back to controller. But I always get the below error:
Failed to load resource: the server responded with a status of 404 (Not Found)
Below is my controller code:
app.controller("logicCtrl",['$scope','getDestinationService','getVehicleService','getTokenService','getResultService','$http',
function($scope, getDestinationService,getVehicleService,getTokenService,getResultService,$http){
$scope.getResult = function(){
var resultPromise = getResultService.getFinalResult($scope.request_data);
resultPromise.then(function(result){
$scope.result = result.data;
console.log("result:"+$scope.result);
});
}
});
And this is my factory method:
app.factory("getResultService",['$http','$q',function($http, $q){
var getResultApi = "https://findfalcone.herokuapp.com/find";
var headers = {'Accept' : 'application/json'};
var getFinalResult = function(request_data){
var deferred = $q.defer();
var request_data = JSON.stringify(request_data);
return $http({
url: getResultApi,
method: "POST",
data: request_data,
headers: {'Accept' : 'application/x-www-form-urlencoded'}
}).then(function (response) {
if (typeof response.data === 'object') {
deferred.resolve(response.data);
} else {
deferred.reject(response.data);
}
}).catch(function(response) {
return deferred.reject(response.data);
});
return deferred.promise;
};
return { getFinalResult: getFinalResult };
}]);
Edit: As some people are directly hitting the URL in browser and saying URL is not working. It won't work this way as it is post call, not get call. I tried testing this URL in Postman and it is working absolutely fine.
Here is the screenshot:
In the getFinalResult you are overwriting the Accept header as application/x-www-form-urlencoded where as it should be application/json. You are not using the headers variables declared earlier.
Disclaimer: I wrote the Finding Falcone backend application.
This is my angular configuration for appending keycloak token with every HTTP request.
module.factory('authInterceptor', function($q, Auth) {
return {
request: function (config) {
var deferred = $q.defer();
if (Auth.authz.token) {
Auth.authz.updateToken(5).success(function() {
config.headers = config.headers || {};
config.headers.Authorization = 'Bearer ' + Auth.authz.token;
deferred.resolve(config);
}).error(function() {
deferred.reject('Failed to refresh token');
});
}
return deferred.promise;
}
};
});
module.config(["$httpProvider", function ($httpProvider) {
$httpProvider.interceptors.push('authInterceptor');
}]);
This is the request I sending to the backend. It seems the request not adding keycloak token, so I'm getting 403 forbidden error.
var formData = new FormData(file);
formData.append('file', file);
return $http({
method: 'POST',
url: API_BASE + '/uploadEmployeeDetails/excelUpload',
headers: {
'Content-Type': undefined
},
data: formData,
transformRequest: function(data, headersGetterFunction) {
return data;
}
});
Backend security config
Since you are able to send the token to the back-end as you can see from the network tab of the browser.
The issue is in the api side on handling the csrf token
If the csrf token is enabled by default you should disable it.
Here is the code with your help, to disable it
http.csrf().disable();
http.addFilterBefore(new CORSFilter(), ChannelProcessingFilter.class)
.authorizeRequests().antMatchers("/**")
.hasAnyRole("ORG_ADMIN", "EMPLOYEE", "PARENT", "STUDENT")
.anyRequest().permitAll();
This is my sample, i'm not get response headers, it returns undefined
I'm trying to get custom response headers like reponse.header['X-auth-token'] but it returns undefined
I'm new to angular js, Please share your idea
Thanks in advance
//I'm trying to get custom response headers here (using alert(response.headers);)
Controller
UIAppRoute.controller('test', ['$scope', 'checkStatus', function($scope, checkStatus) {
$scope.data = {};
checkStatus.query(function(response) {
alert(response.headers);
angular.forEach(response, function (item) {
alert("resp2"+ item);
});
$scope.data.resp = response;
});
}]);
// sending request to server
service
-------
UIAppResource.factory('checkStatus', function($resource){
var auth = Base64.encode("abcde:abcde");
return $resource(baseURL + "status", {},
{
'query': {
method: 'GET',
headers: {
'Accept':'application/json',
'Content-Type':'application/json',
'Authorization': 'Basic '+ auth,
'Access-Control-Allow-Headers' : 'Origin, X-Requested-With, Content-Type, Accept'
},
isArray: false
}
}
)
how to get headers from response in angularjs ?
Please share your idea
Thanks in advance
response.headers is a function an not a map, so you have to call it instead of accessing it via a key.
response.headers('headerName') should give you the respective header.
See also http://code.angularjs.org/1.2.16/docs/api/ng/service/$http
For $resource see http://code.angularjs.org/1.2.16/docs/api/ngResource/service/$resource
var User = $resource('/user/:userId', {userId:'#id'});
User.get({userId:123}, function(u, headers){
alert(headers('X-Internal-Auth-Token'))
});
});
the first param of function is the returned data, the second param is headers;
so you should write as follow:
checkStatus.query(function(data,headers) {
console.log(headers('xxxx'));
});
I'm trying to send the post request to server with post data
it's sent the request to the server, but not in right format
request url like /rest/api/modifyuser/?currentPassword=admin&newPassword=admin
it's like GET request - (may be this is problem)
I'm new to angularjs . please share idea to solve this problem
Here is my code
In controller
var currentPass = "admin";
var newPass = "admin";
var confirmPass = "admin";
var authToken = "abcdef";
User.changePassword(currentPass, newPass, confirmPass, authToken, function(response) {
angular.forEach(response, function (item) {
alert("resp"+ item);
});
});
In services
UIAppResource.factory('User', function($resource) {
return {
changePassword: function(currentPass, newPass, confirmPass, authtoken, callback) {
var Resq = $resource(baseURL + "modifyuser", {}, {
'query': {
method: 'POST',
params: {
'currentPassword': currentPass,
'newPassword': newPass,
'confirmPassword': confirmPass
},
headers: {
'Accept':'application/json',
'Content-Type':'application/json',
'X-Internal-Auth-Token': authtoken
},
isArray: false
}
});
Resq.query(callback);
}
};
});
Thanks in advance
I dont want to say you are doing it all wrong.. but you are def. abusing things. The default way to POST something with ng-resource is to use save. Second, the default way to send data is to instantiate a $resource factory with the data you want. See _resource below. We pass the data we want, and it will automagically convert it and if its a POST send it in the body, or in the case of a GET it will turn into query parameters.
UIAppResource.factory('User', function($resource) {
return {
changePassword: function(currentPass,
newPass,
confirmPass,
authtoken,
callback
) {
var Resq = $resource(baseURL + "modifyuser", {}, {
'save': {
method: 'POST',
headers: {
'Accept':'application/json',
'Content-Type':'application/json',
'X-Internal-Auth-Token': authtoken
}
}
});
var _resource = new Resq({
'currentPassword': currentPass,
'newPassword': newPass,
'confirmPassword': confirmPass
});
_resource.$save(callback);
}
};
});
I know how to intercept ALL requests, but I only want to intercept requests from my resources.
Does anyone know how to do this?
services.config(['$httpProvider',function($httpProvider) {
$httpProvider.interceptors.push('myHttpInterceptor');
}]);
services.factory("userPurchased", function ($resource) {
return $resource("/api/user/purchases/:action/:item",
{},
{
'list': {method: 'GET', params: {action: 'list'}, isArray: false},
'save': {method: 'PUT', params: {item: '#item'}},
'remove': {method: 'DELETE', params: {item: '#item'}},
}
);
});
services.factory('myHttpInterceptor', function($q,$rootScope) {
// $rootScope.showSpinner = false;
return {
response: function(response) {
$rootScope.showSpinner = false;
// do something on success
console.log('success');
console.log('status', response.status);
//return response;
return response || $q.when(response);
},
responseError: function(response) {
// do something on error
$rootScope.showSpinner = true;
console.log('failure');
console.log('status', response.status)
//return response;
return $q.reject(response);
}
};
});
If you want to intercept only requests from specific resources, you can use optional interceptor property of $request action. Angular's documentation see here (Usage>actions)
JavaScript
angular.module('app', ['ngResource']).
factory('resourceInterceptor', function() {
return {
response: function(response) {
console.log('response intercepted: ', response);
}
}
}).
factory('resourceService', ['$resource', 'resourceInterceptor', function($resource, resourceInterceptor) {
return $resource(":name",
{},
{
'list': {method: 'GET', isArray: false, interceptor: resourceInterceptor}
}
);
}]).
run(['resourceService', '$http', function(resourceService, $http) {
resourceService.list({name: 'list.json'}); // <= intercepted
$http.get('list.json'); // <= not intercepted
}]);
Plunker: http://plnkr.co/edit/xjJH1rdJyB6vvpDACJOT?p=preview
The only way I know of doing this it to just filter out the requests you want in the response handler.
e.g.
...
response: function(response) {
if(response.config.url.startsWith('/api/')) {
//Do your custom processing here
}
return response;
}
...
Polyfill for string.startsWith()
//Taken from http://stackoverflow.com/questions/646628/javascript-startswith
if (typeof(String.prototype.startsWith) === 'undefined') {
String.prototype.startsWith = function(str) {
return this.slice(0, str.length) === str;
};
}
My preferred way is to use an HTTP interceptor which replaces a "magic" Authorization header with the current OAuth token. The code below is OAuth specific, but remedying that is a simple exercise for the reader.
// Injects an HTTP interceptor that replaces a "Bearer" authorization header
// with the current Bearer token.
module.factory('oauthHttpInterceptor', function (OAuth) {
return {
request: function (config) {
if (config.headers.Authorization === 'Bearer') {
config.headers.Authorization = 'Bearer ' + btoa(OAuth.accessToken);
}
return config;
}
};
});
module.config(function ($httpProvider) {
$httpProvider.interceptors.push('oauthHttpInterceptor');
});
/**object single interceptor**/
function SingleCallInterceptor(callbacks){
this.receive=function(response) {
switch (response.status) {
case 200:
callbacks.success(apiResponse);
break;
default :
callbacks.error(response);
}
}
}
var successfn=function(response){ //i have my response}
var errorfn=function(response){ //i have my error}
var responseInterceptor=new SingleCallInterceptor({success:successfn,error:errorfn});
$http({
url: "www.itsdirtysolutioniknow.it,
method: "GET",
dataType: "JSONP",
}).then(responseInterceptor.receive,responseInterceptor.receive);
By default angular sends and receives application/json headers. You can get this on the HTTP response header like :
services.config(['$httpProvider',function($httpProvider) {
$httpProvider.interceptors.push('myHttpInterceptor');
}]);
services.factory("userPurchased", function ($resource) {
return $resource("/api/user/purchases/:action/:item",
{},
{
'list': {method: 'GET', params: {action: 'list'}, isArray: false},
'save': {method: 'PUT', params: {item: '#item'}},
'remove': {method: 'DELETE', params: {item: '#item'}},
}
);
});
services.factory('myHttpInterceptor', function($q,$rootScope) {
// $rootScope.showSpinner = false;
return {
response: function(response) {
// use this line to if you are receiving json, else use xml or any other type
var isJson = response.config.headers.Accept.indexOf('json')>-1;
$rootScope.showSpinner = false;
// do something on success
console.log('success');
console.log('status', response.status);
//return response;
return response || $q.when(response);
},
responseError: function(response) {
// use this line to if you are receiving json, else use xml or any other type
var isJson = response.config.headers.Accept.indexOf('json')>-1;
// do something on error
$rootScope.showSpinner = true;
console.log('failure');
console.log('status', response.status)
//return response;
return $q.reject(response);
}
};
});
I just came across an issue where googleapis also uses an Authorization header, and was throwing a 401 response because the JWT I use on my server wasn't valid for their server (obviously), and my code was set to automatically remove my token and redirect the person to the login page. (It wasn't written super well, since ANY 401 response would log my user out).
I just came up with this solution in my request method in the interceptor, which I think works pretty well:
.service('authInterceptor', ["$q", "$location", "tokenService", function($q, $location, tokenService){
this.request = function(config) {
// console.log($location.host());
var token = tokenService.getToken();
if(token && config.url.indexOf($location.host()) > -1) {
config.headers = config.headers || {};
config.headers.Authorization = "Bearer " + token
}
return config
}
this.responseError = function(response) {
// console.log(response.config.url)
if (response.status === 401) {
tokenService.removeToken();
$location.path('/login')
}
return $q.reject(response);
}
}])
The request method checks if I have a token in local storage AND if the request url is being made to the same host (which I get from $location.host()) as the one my page is being served up on. This works for localhost as well as whatever URL I end up deploying my site on.
I haven't done much testing with this, so if anyone finds a flaw in this please let me know :)
I know it is an old question but I wanted to provide a solution if you have pushed multiple $http Interceptors and want them to continue working, return your response so the Interceptor chain continues:
module.factory('resourceInterceptor', ['$q', function($q) {
return {
response: function(response) {
// do your conditional logic here
if (...) {
return $q.resolve(response);
}
},
responseError: function(response) {
// do your conditional logic here
if (...) {
return $q.reject(response);
}
}
};
}]);