我的问题是我的AngularJS HTTP GET请求没有发送HTTP头。但是,对于HTTP POST,我确实看到正在设置标头。这些HTTP请求通过CORS。
虽然在这个问题上有很多SO帖子,但我已经尝试过了,但没有一个能够奏效。其中一个解决方案表明,如果数据字段为空,则不会发送HTTP标头,并且我已经尝试过添加空数据值的建议(顺便说一句,这对于HTTP GET请求并不是真正意义上的),但是仍然,HTTP标头不成功。
另一方面,我可能会辩称,这个帖子/问题可能值得称之为“不重复”(来自其他SO帖子)因为它处理HTTP GET(而不是HTTP POST)和CORS(相反)不CORS)。
这是我的技术堆栈。
要启用CORS,我按照http://enable-cors.org/server_expressjs.html中的示例进行操作。我的NodeJS服务器应用程序如下所示。
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);
});
我的AngularJS服务如下所示。
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;
}]);
请注意
authenticate
方法,这是一个HTTP POST getUsers
,这是一个HTTP GET data: ''
使用Fiddler,authenticate
我看到以下HTTP请求。
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"}
对于getUsers
,我看到以下HTTP请求。
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
我是否遗漏了一些有关HTTP GET over CORS的内容,而HTTP标头未被发送?
答案 0 :(得分:4)
根据您的HTTP请求,会向您的getUsers
方法的CORS API icase触发OPTIONS请求。
说到CORS,有两种请求
简单请求
简单的跨站点请求是满足以下所有条件的请求:
唯一允许的方法是:
除了用户代理自动设置的标头外,唯一允许手动设置的标头是:
Content-Type标头唯一允许的值是:
预检请求
如果您发出任何违反简单请求条件的请求,则会发送“预检”OPTIONS请求,以确定实际请求是否可以安全发送。特别是,如果出现以下情况,请求将被预先指示:< / p>
有关预检请求的详细信息,请参阅此MDN link。
我相信这就是你的情况。即使您正在制作简单的GET请求,也要添加2个自定义标头X-Requested-With
&amp; x-access-token
这使得有必要验证API的安全性,因此浏览器会发送预检的OPTIONS请求。只有在收到有效响应时,浏览器才会继续使用您的GET请求。
在您的NodeJS服务器代码中,您只处理对/authenticate
的POST请求和对/users
的GET请求,因此在OPTIONS请求的情况下,它将转到您所在的默认处理程序检查令牌,如果它不可用,则回复403
。因此,我建议您更改代码以处理OPTIONS
请求。