I need your help, dunno no how to solve it, I'm using MEAN stack for simple app, that has a field with input tag inside and when i fill it the data is sending to server and save in db, the problem is that post request cant reach the server.
heres is my post:
$http({
method: 'POST',
url: 'http://localhost:3000/api/message',
headers: {'Content-Type': 'application/json'},
data: JSON.stringify({msg: $scope.message})
}).
success(function(response) {
console.log("Success " + JSON.stringify(response));
}).
error(function(response) {
console.log("Error " + JSON.stringify(response));
});
server side:
app.post('/api/message', function(req,res) {
var message = new Message(req.body);
message.save();
res.status(200);
})
app.get('/api/message', function(req,res) {
Message.find(function(err,message) {
if(err) {
res.send(err);
} else {
res.json(message);
}
})
})
and this is what i get in browser dev tool
Request URL:http://localhost:3000/api/message
Request Headers
!Provisional headers are shown
Accept:application/json, text/plain, */*
Content-Type:application/json
Origin:http://localhost:3000
Referer:http://localhost:3000/?
User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36
Request Payload
view source
{msg: "3232"}
msg:"3232"
Following could be the issue
actual request is not being sent from the code or browser is using cached data
some adBlocker or some extension is blocking the request
try removing http://localhost:3000 from the url
My problem is that HTTP headers are not being sent from my AngularJS HTTP GET requests. However, for a HTTP POST, I do see the headers being set. These HTTP requests are over CORS.
Although there are a lot of SO posts on this problem, I have tried them and none of them worked. One of the solutions suggests that HTTP headers are not sent if the data field is empty, and I've tried the suggestion to add an empty data value (which doesn't really sense for a HTTP GET request, by the way), but still, the HTTP headers do not make it.
On a side note, I might defend that this post/question may merit itself as "not a duplicate" (from the other SO posts) as it deals with HTTP GET (as opposed to HTTP POST) and CORS (as opposed to not-CORS).
Here is my technology stack.
NodeJS v4.2.2
Express v4.13.3
AngularJS v1.4.0
To enable CORS, I followed the example here http://enable-cors.org/server_expressjs.html. My NodeJS server application looks like the following.
var express = require('express');
var bodyParser = require('body-parser');
var morgan = require('morgan');
var jwt = require('jsonwebtoken');
var port = process.env.PORT || 8080;
var router = express.Router();
var app = express();
app.set('secret', 'mySecret');
app.use(express.static('public'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(morgan('dev'));
app.use('/api', router);
router.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, x-access-token');
res.header('Access-Control-Allow-Methods', 'POST, PUT, GET, OPTIONS, DELETE');
next();
});
router.post('/authenticate', function(req, res) {
var username = req.body.username;
var pw = req.body.password;
if(username !== 'root') {
res.json({
success: false,
message: 'User not found'
});
} else if(pw !== 'root') {
res.json({
success: false,
message: 'Password wrong'
});
} else {
var user = {
username: username,
pw: pw
};
var token = jwt.sign(user, app.get('secret'), {
expiresIn: 60 * 60 * 24 * 365
});
res.json({
success: true,
message: 'Enjoy your token!',
token: token
});
}
});
router.use(function(req, res, next) {
/* according to comments, have to ignore OPTIONS request from protection */
if('OPTIONS' === req.method) { next(); return; } //original post modified here to show, after adding this line, the OPTIONS is accessible, then the GET does actually send the required HTTP header
if('/api/authenticate' === req.originalUrl) {
next();
return;
}
var token = req.body.token || req.params['token'] || req.headers['x-access-token'];
if(token) {
jwt.verify(token, app.get('secret'), function(err, decoded) {
if(err) {
return res.json({
success: false,
message: 'Failed to authenticate token'
});
} else {
req.decoded = decoded;
next();
}
})
} else {
return res.status(403).send({
success: false,
message: 'No token provided'
});
}
});
router.get('/users', function(req, res) {
res.json([
{ fname: 'john', lname: 'doe' },
{ fname: 'jane', lname: 'smith' }
]);
})
var server = app.listen(port, function() {
var host = server.address().address;
var port = server.address().port;
console.log('Example app listening at http://%s:%s', host, port);
});
My AngularJS service looks like the following.
myServices.factory('HomeService', ['$resource', '$http', '$location', '$cookies', 'conf', function($resource, $http, $location, $cookies, conf) {
var svc = {};
svc.getRestUrl = function() {
return 'http://localhost:8080';
};
svc.sendData = function(url, data, method) {
var restUrl = svc.getRestUrl() + url;
var options = {
method: method,
url: restUrl,
withCredentials: false
};
var token = $cookies.get('token');
if(_.isEmpty(token)) {
options.headers = {
'X-Requested-With': 'XMLHttpRequest'
};
} else {
options.headers = {
'X-Requested-With': 'XMLHttpRequest',
'x-access-token': token
};
}
if(data) {
options.data = data;
} else {
options.data = '';
}
return $http(options);
}
svc.getData = function(url) {
return svc.sendData(url, null, 'GET');
};
svc.postData = function(url, data) {
return svc.sendData(url, data, 'POST');
};
svc.authenticate = function(username, password) {
var data = JSON.stringify({
username: username,
password: password
});
return svc.postData('/api/authenticate', data);
};
svc.getUsers = function() {
return svc.getData('/api/users');
};
return svc;
}]);
Note
for the service's authenticate method, this is a HTTP POST
for the service's getUsers, this a HTTP GET
when there is no data to send (HTTP GET), the data is set to empty data: ''
Using Fiddler, for authenticate I see the following HTTP request.
POST http://localhost:8080/api/authenticate HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 37
Accept: application/json, text/plain, */*
Origin: http://localhost
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36
Content-Type: application/json;charset=UTF-8
Referer: http://localhost/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
{"username":"root","password":"root"}
For getUsers, I see the following HTTP request.
OPTIONS http://localhost:8080/api/users HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Access-Control-Request-Method: GET
Origin: http://localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36
Access-Control-Request-Headers: accept, x-access-token, x-requested-with
Accept: */*
Referer: http://localhost/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
Am I missing something here with regards to HTTP GET over CORS that the HTTP headers are not being sent?
According to your HTTP request, an OPTIONS request is fired to your CORS API icase of your getUsers method.
When it comes to CORS, there are 2 kinds of requests
Simple Requests
Preflighted Requests
Simple requests
A simple cross-site request is one that meets all the following conditions:
The only allowed methods are:
GET
HEAD
POST
Apart from the headers set automatically by the user agent, the only headers which are allowed to be manually set are:
Accept
Accept-Language
Content-Language
Content-Type
The only allowed values for the Content-Type header are:
application/x-www-form-urlencoded
multipart/form-data
text/plain
Preflighted requests
In case you make any request which violates the conditions of a simple request, then a "preflighted" OPTIONS request is sent in order to determine whether the actual request is safe to send.In particular, a request is preflighted if:
It uses methods other than GET, HEAD or POST. Also, if POST is used
to send request data with a Content-Type other than
application/x-www-form-urlencoded, multipart/form-data, or
text/plain, e.g. if the POST request sends an XML payload to the
server using application/xml or text/xml, then the request is
preflighted.
It sets custom headers in the request (e.g. the request uses a header
such as X-PINGOTHER)
For more details about Preflighted Requests, you can refer to this MDN link.
I believe this is what is happening in your case. Even though you're making a simple GET request, you're adding 2 custom headers X-Requested-With & x-access-token which makes it necessary to validate the safety of your API, so a preflighted OPTIONS request is sent by the browser. The browser will continue with your GET request only if it receives valid response.
In your NodeJS server code, you're handling only POST requests to /authenticate and GET requests to /users, so in case of an OPTIONS request, it's going to the default handler where you're checking for token and if it's not available, you respond with a 403. So I suggest you change your code to handle OPTIONS request as well.
When I use $resource for a REST login, the transformRequest doesn't add the Authorization header as intended. Using a $.ajax call it does work as intended.
So using:
$scope.login2 = function() {
function setHeader(xhr){xhr.setRequestHeader("Authorization", "Basic " + btoa($scope.gebruikersnaam + ':' + $scope.wachtwoord))}
$.ajax({type: "POST", url: "http://localhost:8000/authview/", beforeSend: setHeader}).
fail(function(resp){
console.log('bad credentials.')
}).
done(function(resp){
console.log('welcome ' + resp.email)
})
}
I get the authorization header added to the request:
Origin: http://localhost
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36
Authorization: Basic YWRtaW46cGFzc3dvcmQ=
But when doing:
$scope.login = function() {
api.auth.login($scope.getCredentials()).
$promise.
then(function(data){
// on good username and password
$scope.gebruikersnaam = data.username;
}).
catch(function(data){
// on incorrect username and password
alert(data.data.detail);
});
};
where "api.auth.login" is defined like:
kmregistratieApp.factory('api', function($resource){
function add_auth_header(data, headersGetter){
var headers = headersGetter();
headers['Authorization'] = ('Basic ' + btoa(data.username + ':' + data.password));
}
return {
auth: $resource('http://localhost:8000/authview/', {}, {
login: {method: 'POST', transformRequest: add_auth_header},
logout: {method: 'DELETE'}
}),
users: $resource('http://localhost:8000/authview/', {}, {
create: {method: 'POST'}
})
};
});
After "headers['Authorization'] = ('Basic ' + ..." (when debugging) I can see it sitting in headersGetter:
headers: Object
Authorization: "Basic YWRtaW46cGFzc3dvcmQ="
accept: "application/json, text/plain, */*"
content-type: "application/json;charset=utf-8"
But it doesn't turn up in the Network tab when inspecting the headers.
So my question is why doesn't the $resource way of working not add the Authorization header?
TransformRequest is not meant to be used to modify headers.
See AngularJS changelog. Scroll a bit downwards and you will see this:
transformRequest functions can no longer modify request headers.
HTTP headers can only be specified when using $http. Example:
$http.post('/someUrl', data, { headers: { 'Authorization': 'Basic'+key } });
I am trying to build a simple application in angular-js and spring security.I am using basic authentication.Whenever browsing home page ,i am getting basic authentication pop-up for user name password.If i cancel it and login with correct password,application is working fine.But if i enter wrong password the same basic authentication pop -up is coming.I am sending X-Requested-With header in every request and it is visible in header fiends also.Any one has any idea,what's going wrong here?
Angular :
'use strict';
var todoApp=angular.module('todoApp',['ngRoute']);
todoApp.config(['$routeProvider','$httpProvider',function($routeProvider,$httpProvider){
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
$routeProvider.when('/',{
templateUrl:'resources/templates/Home.html',
controller:'HomeController'
}).otherwise({redirectTo:'/'});
}]);
'user strict';
todoApp.controller('NavBarController',function($rootScope, $scope, $http, $location, $route){
$scope.credentials = {};
$scope.login = function() {
authenticate($scope.credentials, function(authenticated) {
if (authenticated) {
console.log("Login succeeded")
$location.path("/");
$scope.error = false;
$rootScope.authenticated = true;
} else {
console.log("Login failed")
$location.path("/");
$scope.error = true;
$rootScope.authenticated = false;
}
})
};
$scope.logout=function(){
$http.post('logout', {}).success(function() {
$rootScope.authenticated = false;
$location.path("/");
}).error(function(data) {
console.log("Logout failed")
$rootScope.authenticated = false;
});
}
var authenticate=function(credentials,callback){
//create headers for request
var headers= credentials? {
authorization:"Basic "
+btoa(credentials.username+":"+credentials.password)}:{};
//request to http basic service
$http.get('user/authenticate',{
headers:headers
}).success(function(data){
if(data.name){
$rootScope.authenticated=true
}else{
$rootScope.authenticated=false;
}
callback && callback($rootScope.authenticated);
}).error(function(data){
$rootScope.authenticated=false;
callback && callback(false);
});
};
authenticate();
});
security configuration:
<sec:http use-expressions="true">
<sec:intercept-url pattern="/" access="permitAll"/>
<sec:intercept-url pattern="/index.html" access="permitAll"/>
<sec:intercept-url pattern="/Home.html" access="permitAll"/>
<sec:intercept-url pattern="/**" access="hasRole('ROLE_USER')"/>
<sec:http-basic/>
</sec:http>
<sec:authentication-manager>
<sec:authentication-provider>
<sec:jdbc-user-service data-source-ref="dataSource" id="userDetailsService"/>
</sec:authentication-provider>
</sec:authentication-manager>
Headers:
Content-Language:en
Content-Length:1160
Content-Type:text/html;charset=utf-8
Date:Fri, 12 Jun 2015 02:46:18 GMT
Server:Apache-Coyote/1.1
WWW-Authenticate:Basic realm="Spring Security Application"
Request Headers
view source
Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8
Cache-Control:no-cache
Connection:keep-alive
Cookie:JSESSIONID=A06CEC616C9A34B915EA298A890C5E80
Host:localhost:9999
Pragma:no-cache
Referer:http://localhost:9999/todoapp/
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.124 Safari/537.36
X-Requested-With:XMLHttpRequest
Sending WWW-Authenticate:Basic realm="Spring Security Application" will cause the browser to show a login form.
You need to serve your initial angular assets without requiring Basic authentication.
I am trying to create a call with AngularJS v1.3.15 into ASP.NET WebAPI (latest from Nuget) to get a list of customers. I can successfully authenticate and get back a token. I add the token to an Authentication header but when I make the call it gets kicked back saying the Authentication header is missing.
The Angular call after I get the token looks like this
$scope.baseUrl = "http://localhost:7800/";
$http({
method: 'GET',
url: $scope.baseUrl + 'customer',
headers: {
'Authorization': $scope.token
}})
I have also tried to utilize the angularjs $resourse
return $resource($scope.baseUrl + 'customer', { }, { 'get': { method: 'GET', isArray: true, headers: { 'Authorization': $scope.token } } });
In the WebApiConfig Register method I have the following setup
var cors = new EnableCorsAttribute("*","*","*");
config.EnableCors(cors);
in a DelegatingHandler I check for the Authorization header like so
var authHeader = request.Headers.Authorization;
From the Angular app it is always null. If I run a check from Fiddler and PostMan I get the Authorization header just fine.
When I press F12 from Chrome and look at the request header these are the results
OPTIONS /customer HTTP/1.1
Host: localhost:7800
Connection: keep-alive
Access-Control-Request-Method: GET
Origin: http://localhost:63342
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2376.0 Safari/537.36
Access-Control-Request-Headers: accept, authorization
Accept: */*
Referer: http://localhost:63342/Test/index.html
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
Fixed my issue, after some searches I found that OPTIONS does not seem to be supported out of the box. I found that if I add NuGet package Microsoft.Owin.Cors and then add
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
to the startup.cs file