我试图在使用Express.js web框架的Node.js应用程序中支持CORS。我已经阅读了谷歌关于如何处理这个问题的小组讨论,并阅读了一些关于CORS如何工作的文章。首先,我这样做(代码是用CoffeeScript语法写的):

app.options "*", (req, res) ->
  res.header 'Access-Control-Allow-Origin', '*'
  res.header 'Access-Control-Allow-Credentials', true
  # try: 'POST, GET, PUT, DELETE, OPTIONS'
  res.header 'Access-Control-Allow-Methods', 'GET, OPTIONS'
  # try: 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept'
  res.header 'Access-Control-Allow-Headers', 'Content-Type'
  # ...

这似乎不管用。似乎我的浏览器(Chrome)没有发送最初的选项请求。当我刚刚更新了块的资源,我需要提交一个跨起源GET请求:

app.get "/somethingelse", (req, res) ->
  # ...
  res.header 'Access-Control-Allow-Origin', '*'
  res.header 'Access-Control-Allow-Credentials', true
  res.header 'Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS'
  res.header 'Access-Control-Allow-Headers', 'Content-Type'
  # ...

它工作(在Chrome)。这也适用于Safari。

我听说……

在实现CORS的浏览器中,每个跨源GET或POST请求之前都有一个OPTIONS请求,用于检查GET或POST是否正常。

所以我的主要问题是,为什么这种情况在我身上没有发生?为什么我的app。options块没有被调用?为什么我需要在我的主app.get块设置标题?


当前回答

如果你想让它特定于控制器,你可以使用:

res.setHeader("X-Frame-Options", "ALLOWALL");
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "POST, GET");
res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");

请注意,这也将允许iframes。

其他回答

为了回答您的主要问题,CORS规范只要求在POST或GET中有任何非简单内容或标头时将OPTIONS调用放在POST或GET之前。

需要CORS飞行前请求(OPTIONS调用)的内容类型是任何内容类型,除了以下内容:

应用程序/ x-www-form-urlencoded 多部分/格式 文本/平原

除了上面列出的内容类型外,任何其他内容类型都将触发飞行前请求。

对于header,除了以下以外的任何Request header都会触发一个pre-flight Request:

接受 接收语言 内容语言 内容类型 DPR 保存数据 Viewport-Width 宽度

任何其他请求头将触发预飞行请求。

因此,您可以添加一个自定义报头,如:x-Trigger: CORS,它应该触发飞行前请求并点击OPTIONS块。

参见MDN Web API参考- CORS预飞请求

首先在项目中简单地安装cors。 使用terminal(命令提示符)和cd到您的项目目录,并运行以下命令:

npm install cors --save

然后获取server.js文件并更改代码,在其中添加以下内容:

var cors = require('cors');


var app = express();

app.use(cors());

app.use(function(req, res, next) {
   res.header("Access-Control-Allow-Origin", "*");
   res.header('Access-Control-Allow-Methods', 'DELETE, PUT, GET, POST');
   res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
   next();
});

这对我很有效。

我们可以避免CORS,而是将请求转发到另一个服务器:

// config:
var public_folder = __dirname + '/public'
var apiServerHost = 'http://other.server'

// code:
console.log("starting server...");

var express = require('express');
var app = express();
var request = require('request');

// serve static files
app.use(express.static(public_folder));

// if not found, serve from another server
app.use(function(req, res) {
    var url = apiServerHost + req.url;
    req.pipe(request(url)).pipe(res);
});

app.listen(80, function(){
    console.log("server ready");
});

简单很难:

 let my_data = []
const promise = new Promise(async function (resolve, reject) {
    axios.post('https://cors-anywhere.herokuapp.com/https://maps.googleapis.com/maps/api/directions/json?origin=33.69057660000001,72.9782724&destination=33.691478,%2072.978594&key=AIzaSyApzbs5QDJOnEObdSBN_Cmln5ZWxx323vA'
        , { 'Origin': 'https://localhost:3000' })
        .then(function (response) {
            console.log(`axios response ${response.data}`)
            const my_data = response.data
            resolve(my_data)
        })
        .catch(function (error) {
            console.log(error)
            alert('connection error')
        })
})
promise.then(data => {
    console.log(JSON.stringify(data))
})

我在我的server.js文件中使用了下面的方法。这里的关键部分是从请求头中获取起源,然后在服务器响应中允许它,此时我们可以设置将返回的头,包括如果找到匹配则允许的起源。

    **const origin = req.headers.origin;**

      let decoder = new StringDecoder('utf-8');
      let buffer = '';
      req.on('data', function (data) {
        buffer += decoder.write(data);
      });
      req.on('end', function () {
        buffer += decoder.end();

        let chosenHandler = typeof (server.router[trimmedPath]) !== 'undefined' ? server.router[trimmedPath] : handlers.notFound;

const data = { ....data object vars}

// should be wrapped in try catch block
      chosenHandler(data, function (statusCode, payload, contentType) {
        server.processHandlerResponse(res, method, trimmedPath, statusCode, payload, contentType, **origin**);


server.processHandlerResponse = function (res, method, trimmedPath, statusCode, payload, contentType, origin) {
  contentType = typeof (contentType) == 'string' ? contentType : 'json';

  statusCode = typeof (statusCode) == 'number' ? statusCode : 200;

  let payloadString = '';
  if (contentType == 'json') {
    res.setHeader('Content-Type', 'application/json');

    const allowedOrigins = ['https://www.domain1.com', 'https://someotherdomain','https://yetanotherdomain',
    ...// as many as you need
  ];
    **if (allowedOrigins.indexOf(origin) > -1) {
        res.setHeader('Access-Control-Allow-Origin', origin);
    }**
    payload = typeof (payload) == 'object' ? payload : {};
    payloadString = JSON.stringify(payload);
  }

... //  if (other content type) ...rinse and repeat..