在AngularJS应用程序

时间:2015-12-31 05:47:18

标签: angularjs node.js express cors angularjs-http

我的问题是我的AngularJS HTTP GET请求没有发送HTTP头。但是,对于HTTP POST,我确实看到正在设置标头。这些HTTP请求通过CORS。

虽然在这个问题上有很多SO帖子,但我已经尝试过了,但没有一个能够奏效。其中一个解决方案表明,如果数据字段为空,则不会发送HTTP标头,并且我已经尝试过添加空数据值的建议(顺便说一句,这对于HTTP GET请求并不是真正意义上的),但是仍然,HTTP标头不成功。

另一方面,我可能会辩称,这个帖子/问题可能值得称之为“不重复”(来自其他SO帖子)因为它处理HTTP GET(而不是HTTP POST)和CORS(相反)不CORS)。

这是我的技术堆栈。

  • NodeJS v4.2.2
  • Express v4.13.3
  • AngularJS v1.4.0

要启用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
  • 当没有要发送的数据(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标头未被发送?

1 个答案:

答案 0 :(得分:4)

根据您的HTTP请求,会向您的getUsers方法的CORS API icase触发OPTIONS请求。

说到CORS,有两种请求

  1. 简单请求
  2. 预先发出的请求
  3. 简单请求

    简单的跨站点请求是满足以下所有条件的请求:

    唯一允许的方法是:

    • GET
    • HEAD
    • POST

    除了用户代理自动设置的标头外,唯一允许手动设置的标头是:

    • 接受
    • 接受语言
    • 内容的语言
    • 内容类型

    Content-Type标头唯一允许的值是:

    • 应用程序/ x-WWW窗体-urlencoded
    • 的multipart / form-data的
    • 文本/纯

    预检请求

    如果您发出任何违反简单请求条件的请求,则会发送“预检”OPTIONS请求,以确定实际请求是否可以安全发送。特别是,如果出现以下情况,请求将被预先指示:< / p>

    • 它使用GET,HEAD或POST以外的方法。此外,如果使用POST 使用除以外的Content-Type发送请求数据 application / x-www-form-urlencoded,multipart / form-data,或 text / plain,例如如果POST请求发送XML有效负载 服务器使用application / xml或text / xml,然后请求是 预检。
    • 它在请求中设置自定义标头(例如,请求使用标头 如X-PINGOTHER)

    有关预检请求的详细信息,请参阅此MDN link

    我相信这就是你的情况。即使您正在制作简单的GET请求,也要添加2个自定义标头X-Requested-With&amp; x-access-token这使得有必要验证API的安全性,因此浏览器会发送预检的OPTIONS请求。只有在收到有效响应时,浏览器才会继续使用您的GET请求。

    在您的NodeJS服务器代码中,您只处理对/authenticate的POST请求和对/users的GET请求,因此在OPTIONS请求的情况下,它将转到您所在的默认处理程序检查令牌,如果它不可用,则回复403。因此,我建议您更改代码以处理OPTIONS请求。