为什么不将CORS标头添加到OPTIONS路由允许浏览器访问我的API?


653

我试图在使用Express.js Web框架的Node.js应用程序中支持CORS。我读过了有关如何处理此问题的Google小组讨论,并阅读了一些有关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)没有发送初始的OPTIONS请求。当我刚刚更新资源块时,我需要向以下站点提交跨域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块中设置标题?


2
CoffeeScript的黄金法则是:“这只是JavaScript”。
SSH

Answers:


185

要回答您的主要问题,如果POST或GET中包含任何非简单的内容或标头,则CORS规范仅要求OPTIONS调用位于POST或GET之前。

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

  1. application/x-www-form-urlencoded
  2. multipart/form-data
  3. text/plain

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

至于标头,除以下内容外,任何请求标头都会触发预检请求:

  1. Accept
  2. Accept-Language
  3. Content-Language
  4. Content-Type
  5. DPR
  6. Save-Data
  7. Viewport-Width
  8. Width

其他任何请求标头都将触发飞行前请求。

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

请参阅《MDN Web API参考-CORS预检请求》


11
你能举个例子吗?
Glen Pierce

3
我链接到的页面似乎有很多示例。你能告诉我你认为缺少哪个例子吗?
Dobes Vandermeer '18

7
但总的来说,仅链接的答案很脆弱,因为它们随时可能被破坏。也就是说,这个答案似乎足够好,因为它突出显示了OPTIONS不发送块的一般条件。如果它具有被接受的列表HEADERS,或者content-types需要OPTIONS,等等,那将是很好的,但这是一个好的开始
dwanderson

668

我发现最简单的方法是使用node.js包cors。最简单的用法是:

var cors = require('cors')

var app = express()
app.use(cors())

当然,有很多方法可以根据您的需要配置行为。上面链接的页面显示了许多示例。


1
当我将其与凭据一起使用时,它对我来说失败。:(其他所有工作都像是一种魅力。。但是如果凭据设置为true失败,它对我没有用
Sambhav Sharma 2014年

它对ajax请求的工作正常。我想要针对脚本标签和iFrame的CORS实现,因为在这些请求中,请求标头中不存在Origin :(如何实现此功能?
akashPatra 2014年

59
您还需要设置cors({credentials: true, origin: true})
nlawson 2014年

2
如何在此处启用选项预检?
chovy

@nlawson ahh您救了我
Adrin

446

尝试将控制权传递给下一条匹配的路线。如果Express首先匹配app.get路由,则除非您这样做否则请注意使用next),否则它将不会继续进入选项路由:

app.get('somethingelse', function(req, res, next) {
    //..set headers etc.

    next();
});

在组织CORS方面,我将其放在对我来说运行良好的中间件中:

//CORS middleware
var allowCrossDomain = function(req, res, next) {
    res.header('Access-Control-Allow-Origin', 'example.com');
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
    res.header('Access-Control-Allow-Headers', 'Content-Type');

    next();
}

//...
app.configure(function() {
    app.use(express.bodyParser());
    app.use(express.cookieParser());
    app.use(express.session({ secret: 'cool beans' }));
    app.use(express.methodOverride());
    app.use(allowCrossDomain);
    app.use(app.router);
    app.use(express.static(__dirname + '/public'));
});

1
我相信OPTIONS会在GET之前发生,但是如果您正在执行POST-没有OPTIONS请求...
Nick

21
config.allowedDomains逗号分隔的字符串还是数组?
pixelfreak

2
config.allowedDomains应该是一个由空格分隔的数组
mcfedr

1
通过简单地重新排列明确的中间件顺序就删除了多余的会话。另一方面,这需要更多的安全性。如果来源不在允许的域中,则请求仍会被处理,只有浏览器无法看到请求,并且来源会被欺骗。我的建议是进行检查,如果原点不在允许的列表中,请立即返回403。同样,正在提供任何敏感信息,通过会话验证用户。
Xerri

1
@mcfedr您能否解释一下空格分隔的数组是什么意思?
pootzko 2014年


84

npm install cors --save

并将这些行添加到您的请求所处的主文件中(保留在任何路由之前)。

const cors = require('cors');
const express = require('express');
let app = express();
app.use(cors());
app.options('*', cors());

3
app.options('*', cors())//在其他路线之前包含
Rajeshwar,

52

我做了一个更完整的中间件,适合表达或连接。它支持OPTIONS进行飞行前检查的请求。请注意,它将允许CORS访问任何内容,如果您想限制访问,则可能需要进行一些检查。

app.use(function(req, res, next) {
    var oneof = false;
    if(req.headers.origin) {
        res.header('Access-Control-Allow-Origin', req.headers.origin);
        oneof = true;
    }
    if(req.headers['access-control-request-method']) {
        res.header('Access-Control-Allow-Methods', req.headers['access-control-request-method']);
        oneof = true;
    }
    if(req.headers['access-control-request-headers']) {
        res.header('Access-Control-Allow-Headers', req.headers['access-control-request-headers']);
        oneof = true;
    }
    if(oneof) {
        res.header('Access-Control-Max-Age', 60 * 60 * 24 * 365);
    }

    // intercept OPTIONS method
    if (oneof && req.method == 'OPTIONS') {
        res.send(200);
    }
    else {
        next();
    }
});

嘿,我遇到了您的解决方案,想知道如果未检测到标头之一,是否应该将'oneof'标志设置为false?
Leonidas

1
某些请求将不具有所有标题。具体来说,GET请求将由浏览器发送,并且当它没有获得正确的allow-origin响应时,会给js错误。而对于POST请求,则首先发送带有allow-method头的OPTIONS请求,然后才发送实际的POST请求。
mcfedr

1
知道了 谢谢。如果req方法是“ options”,您是否曾经因为没有在其中放置res.send(200)而遇到麻烦?
Leonidas

我认为我没有尝试发送其他内容,我想其他任何响应都会导致浏览器拒绝该请求。
mcfedr

就像魅力一样工作,我只想添加一个授权域列表以提高安全性
Sebastien

37

安装expressjs的cors模块。您可以按照以下步骤操作>

安装

npm install cors

简单用法(启用所有CORS请求)

var express = require('express');
var cors = require('cors');
var app = express();
app.use(cors());

有关更多详细信息,请访问https://github.com/expressjs/cors


2
TypeError: Cannot read property 'headers' of undefined最基本的应用设置。
奥利弗·迪克森

您确定您有请求对象吗?:)
代码的

33

做这样的事情:

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

20

使用在不同端口中运行的express + node + ionic完成的测试。

Localhost:8100

Localhost:5000

// CORS (Cross-Origin Resource Sharing) headers to support Cross-site HTTP requests

app.all('*', function(req, res, next) {
       res.header("Access-Control-Allow-Origin", "*");
       res.header("Access-Control-Allow-Headers", "X-Requested-With");
       res.header('Access-Control-Allow-Headers', 'Content-Type');
       next();
});

2
我们需要在哪个文件中添加这些行?
Shefalee Chaudhary

20

首先,只需在您的项目中安装cors。在终端(命令提示符)下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();
});

这对我有用。


4
cors如果您正在做这些res.header事情,则不需要。cors是一个为您处理所有事务的库。删除您的第一行和第三行(使用来代替所有内容cors),您会发现它仍然有效。
thisissami

哎呀,我很确定您真正需要的是这条线res.header("Access-Control-Allow-Origin", "*");
thisissami

不过请记住,这样做会损害您的安全性。:)
thisissami

10

这对我来说很有效,因为它在路由中很容易实现,即时通讯使用meanjs及其正常工作,Safari,Chrome等。

app.route('/footer-contact-form').post(emailer.sendFooterMail).options(function(req,res,next){ 
        res.header('Access-Control-Allow-Origin', '*'); 
        res.header('Access-Control-Allow-Methods', 'GET, POST');
        res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
        return res.send(200);

    });

10

前一段时间,我遇到了这个问题,所以我这样做是为了在我的nodejs应用程序中允许CORS:

首先,您需要 cors使用以下命令进行安装

npm install cors --save

现在,将以下代码添加到您的应用开始文件中,例如(app.js or server.js

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

var cors = require('cors');
var bodyParser = require('body-parser');

//enables cors
app.use(cors({
  'allowedHeaders': ['sessionId', 'Content-Type'],
  'exposedHeaders': ['sessionId'],
  'origin': '*',
  'methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
  'preflightContinue': false
}));

require('./router/index')(app);

在安装cors后尝试了此操作。CORS是不是一个函数
科林rickels

9

在我的index.js补充中:

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

2
我应该发表评论,这Access-Control-Allow-Origin = *意味着您将无法将Cookie发送到服务器,而CORS政策将拒绝它们-来源:developer.mozilla.org/en-US/docs/Web/HTTP/…。因此,如果要使用Cookie,请不要使用此功能。
Eksapsy

7

如果要使其特定于控制器,则可以使用:

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');

请注意,这也将允许iframe。


6

可以参考下面的代码。资料来源:Academind / node-restful-api

const express = require('express');
const app = express();

//acts as a middleware
//to handle CORS Errors
app.use((req, res, next) => { //doesn't send response just adjusts it
    res.header("Access-Control-Allow-Origin", "*") //* to give access to any origin
    res.header(
        "Access-Control-Allow-Headers",
        "Origin, X-Requested-With, Content-Type, Accept, Authorization" //to give access to all the headers provided
    );
    if(req.method === 'OPTIONS'){
        res.header('Access-Control-Allow-Methods', 'PUT, POST, PATCH, DELETE, GET'); //to give access to all the methods provided
        return res.status(200).json({});
    }
    next(); //so that other routes can take over
})

我看到了很多答案,其中有些是很重要的,我尝试在一些其他配置之后使用这部分代码,但由于某些原因,我尝试将代码放在app之后const app = express();可以工作!我认为提及这一点很重要。
rfcabal

4

我最简单的Express 4.2.0解决方案(编辑:在4.3.0中似乎不起作用)是:

function supportCrossOriginScript(req, res, next) {
    res.status(200);
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Content-Type");

    // res.header("Access-Control-Allow-Headers", "Origin");
    // res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    // res.header("Access-Control-Allow-Methods","POST, OPTIONS");
    // res.header("Access-Control-Allow-Methods","POST, GET, OPTIONS, DELETE, PUT, HEAD");
    // res.header("Access-Control-Max-Age","1728000");
    next();
}

// Support CORS
app.options('/result', supportCrossOriginScript);

app.post('/result', supportCrossOriginScript, function(req, res) {
    res.send('received');
    // do stuff with req
});

我想这样做app.all('/result', ...)也可以...


3

最简单的答案是只使用cors软件包

const cors = require('cors');

const app = require('express')();
app.use(cors());

这将全面启用CORS。如果您想学习如何在不使用外部模块的情况下启用CORS,则您真正需要的是一些Express中间件,该中间件设置了“ Access-Control-Allow-Origin”标头。这是允许从浏览器到服务器的跨请求域的最低要求。

app.options('*', (req, res) => {
  res.set('Access-Control-Allow-Origin', '*');
  res.send('ok');
});

app.use((req, res) => {
  res.set('Access-Control-Allow-Origin', '*');
});

2

使用Express Middleware非常适合我。如果您已经在使用Express,则只需添加以下中间件规则。它应该开始工作。

app.all("/api/*", function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With");
  res.header("Access-Control-Allow-Methods", "GET, PUT, POST");
  return next();
});

app.all("/api/*", function(req, res, next) {
  if (req.method.toLowerCase() !== "options") {
    return next();
  }
  return res.send(204);
});

参考


2

下面为我​​工作,希望它能对某人有所帮助!

const express = require('express');
const cors = require('cors');
let app = express();

app.use(cors({ origin: true }));

https://expressjs.com/en/resources/middleware/cors.html#configuring-cors获得了参考


那会进入什么文件?花生酱.js?
Andrew Koper

不知道花生酱.js,对我来说就是express.js。基本上,它应该是您需要Express应用程序,对其进行初始化以及需要路由文件的文件。
Vatsal Shah

1

我发现使用npm请求包(https://www.npmjs.com/package/request)做到这一点非常容易

然后,我基于这篇文章http://blog.javascripting.com/2015/01/17/dont-hassle-with-cors/

'use strict'

const express = require('express');
const request = require('request');

let proxyConfig = {
    url : {
        base: 'http://servertoreach.com?id=',
    }
}

/* setting up and configuring node express server for the application */
let server = express();
server.set('port', 3000);


/* methods forwarded to the servertoreach proxy  */
server.use('/somethingElse', function(req, res)
{
    let url = proxyConfig.url.base + req.query.id;
    req.pipe(request(url)).pipe(res);
});


/* start the server */
server.listen(server.get('port'), function() {
    console.log('express server with a proxy listening on port ' + server.get('port'));
});

1

在打字稿中,如果要使用node.js包cors

/**
* app.ts
* If you use the cors library
*/

import * as express from "express";
[...]
import * as cors from 'cors';

class App {
   public express: express.Application;

   constructor() {
       this.express = express();
       [..]
       this.handleCORSErrors();
   }

   private handleCORSErrors(): any {
       const corsOptions: cors.CorsOptions = {
           origin: 'http://example.com',
           optionsSuccessStatus: 200
       };
       this.express.use(cors(corsOptions));
   }
}

export default new App().express;

如果您不想使用第三方库进行cors错误处理,则需要更改handleCORSErrors()方法。

/**
* app.ts
* If you do not use the cors library
*/

import * as express from "express";
[...]

class App {
   public express: express.Application;

   constructor() {
       this.express = express();
       [..]
       this.handleCORSErrors();
   }

   private handleCORSErrors(): any {
       this.express.use((req, res, next) => {
           res.header("Access-Control-Allow-Origin", "*");
           res.header(
               "Access-Control-ALlow-Headers",
               "Origin, X-Requested-With, Content-Type, Accept, Authorization"
           );
           if (req.method === "OPTIONS") {
               res.header(
                   "Access-Control-Allow-Methods",
                   "PUT, POST, PATCH, GET, DELETE"
               );
               return res.status(200).json({});
           } 
           next(); // send the request to the next middleware
       });
    }
}

export default new App().express;

用于使用app.ts文件

/**
* server.ts
*/
import * as http from "http";
import app from "./app";

const server: http.Server = http.createServer(app);

const PORT: any = process.env.PORT || 3000;
server.listen(PORT);

1
“如果服务器是用打字稿编写的” —否。问题说它是用CoffeeScript编写的。
昆汀

1
@Quentin我只是想在typesript中显示一个替代方案,希望这可以对某人有所帮助。
克服者

1

这与Pat的回答相似,只是我用res.sendStatus(200)完成了区别;而不是next();

该代码将捕获方法类型为OPTIONS的所有请求,并发送回access-control-header。

app.options('/*', (req, res, next) => {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
    res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
    res.sendStatus(200);
});

该代码接受问题中要求的所有来源的CORS。但是,最好将*替换为特定来源,即http:// localhost:8080以防止滥用。

由于我们使用app.options-method而不是app.use-method,因此我们不需要进行此检查:

req.method === 'OPTIONS'

我们可以在其他一些答案中看到这一点。

我在这里找到了答案:http : //johnzhang.io/options-request-in-express


1

您可以使用Express中间件,阻止您的域和方法。

app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", process.env.DOMAIN); // update to match the domain you will make the request from
  res.header("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE");
  res.header(
    "Access-Control-Allow-Headers",
    "Origin, X-Requested-With, Content-Type, Accept"
  );
  next();
});

这帮助我解决了我的问题,谢谢!
Fr0zenOfficial

0

我们可以避免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");
});

1
这不能回答所问的问题
david.barkhuizen

0

我对Web应用程序执行了以下步骤,并获得了成功:

将cors包添加到快递中:

npm install cors --save

在bodyParser配置之后添加以下行。我在添加bodyParser时遇到了一些麻烦:

 // enable cors to the server
const corsOpt = {
    origin: process.env.CORS_ALLOW_ORIGIN || '*', // this work well to configure origin url in the server
    methods: ['GET', 'PUT', 'POST', 'DELETE', 'OPTIONS'], // to works well with web app, OPTIONS is required
    allowedHeaders: ['Content-Type', 'Authorization'] // allow json and token in the headers
};
app.use(cors(corsOpt)); // cors for all the routes of the application
app.options('*', cors(corsOpt)); // automatic cors gen for HTTP verbs in all routes, This can be redundant but I kept to be sure that will always work.

0

最简单的方法是使用以下命令在您的项目中安装cors模块:

npm i --save cors

然后在服务器文件中使用以下命令将其导入:

import cors from 'cors';

然后只需将其用作这样的中间件即可:

app.use(cors());

希望这可以帮助!


0

如果我是@OP,我将更改我的编程范例。

假设由于要向localhost或类似的请求而使这些CORS被阻止。

最终,如果您要部署到像Google Cloud Platformor Heroku或or这样的生产视蛋白,则无需担心诸如允许原产地之类的CORS或生产中的任何东西。

因此,在测试服务器时,只需使用它即可,postman并且在部署服务器然后在客户端上运行之后,您不会受到CORS的阻止。


0

/ *首先,这可能是像我这样的初级开发人员之间的问题:请确保在您的提取方法中使用“ lambda” >>>>“`”而不是“'” !* /

```const response =等待fetch(https://api....);

/ 加号,强烈建议以下文章:https : //developer.edamam.com/api/faq /


0

简单很难:

 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))
})

0

在您的主要js文件中尝试以下操作:

app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header(
  "Access-Control-Allow-Headers",
  "Authorization, X-API-KEY, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Allow-Request-Method"
);
res.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, DELETE");
res.header("Allow", "GET, POST, OPTIONS, PUT, DELETE");
next();
});

这应该可以解决您的问题

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.