为啥向 OPTIONS 路由添加 CORS 标头不允许浏览器访问我的 API?
Posted
技术标签:
【中文标题】为啥向 OPTIONS 路由添加 CORS 标头不允许浏览器访问我的 API?【英文标题】:Why doesn't adding CORS headers to an OPTIONS route allow browsers to access my API?为什么向 OPTIONS 路由添加 CORS 标头不允许浏览器访问我的 API? 【发布时间】:2011-10-27 10:23:54 【问题描述】:我正在尝试在使用 Express.js Web 框架的 Node.js 应用程序中支持 CORS。我已经阅读了 a Google group discussion 关于如何处理这个问题,并阅读了一些关于 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 块中设置标题?
【问题讨论】:
CoffeeScript 的黄金法则是:“它只是 javascript”。 这个答案可能有用:***.com/a/58064366/7059557 尝试使用 CORS 中间件? mydaytodo.com/cors-issue-and-nodejs-proxy 【参考方案1】:我发现最简单的方法是使用 node.js 包cors。最简单的用法是:
var cors = require('cors')
var app = express()
app.use(cors())
当然,有很多方法可以根据您的需要配置行为;上面链接的页面显示了许多示例。
【讨论】:
当我将它与凭据一起使用时,它对我来说失败了。 :( 其他一切都像一个魅力.. 但如果它失败了我没有用凭据设置为 true 它适用于 ajax 请求。我想要脚本标签和 iFrame 的 CORS 实现,因为在这些请求中,请求标头中不存在 Origin :( 如何实现? 你还需要设置cors(credentials: true, origin: true)
如何在此处启用选项预检?
@nlawson ahh 你救了我【参考方案2】:
尝试将控制权传递给下一个匹配的路由。如果 Express 首先匹配 app.get 路由,那么它不会继续到 options 路由,除非你这样做(注意使用 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'));
);
【讨论】:
我相信 OPTIONS 发生在 GET 之前,而如果您正在执行 POST - 没有 OPTIONS 请求...config.allowedDomains
是逗号分隔的字符串还是数组?
config.allowedDomains 应该是一个空格分隔的数组
通过简单地重新排列快速中间件顺序来删除额外的会话。另一方面,这需要更多的安全性。如果源不在允许的域中,则请求仍在处理中,只有浏览器无法看到它,而且源可能被欺骗。我的建议是进行检查,如果来源不在允许列表中,则立即返回 403。此外,是否正在提供任何敏感信息,请通过会话验证用户。
@mcfedr 你能解释一下空格分隔数组是什么意思吗?【参考方案3】:
为了回答您的主要问题,如果 POST 或 GET 中包含任何非简单内容或标题,CORS 规范仅要求 OPTIONS 调用位于 POST 或 GET 之前。
需要 CORS 飞行前请求(OPTIONS 调用)的内容类型是任何内容类型除了以下内容:
application/x-www-form-urlencoded
multipart/form-data
text/plain
除上面列出的内容类型之外的任何其他内容类型都将触发飞行前请求。
至于标头,任何请求标头除了以下将触发飞行前请求:
Accept
Accept-Language
Content-Language
Content-Type
DPR
Save-Data
Viewport-Width
Width
任何其他请求标头都会触发飞行前请求。
因此,您可以添加一个自定义标头,例如:x-Trigger: CORS
,这应该会触发飞行前请求并点击 OPTIONS 块。
见MDN Web API Reference - CORS Preflighted requests
【讨论】:
你能举个例子吗? 我链接到的页面似乎有很多例子。你能告诉我你认为缺少什么例子吗? 一般来说,仅链接的答案很脆弱,因为它们随时可能被破坏。也就是说,这个答案似乎足够好,因为它突出了OPTIONS
块不发送的一般条件。如果它有接受 HEADERS
的列表,或者 content-types
需要 OPTIONS
等的列表会很好,但这是一个好的开始【参考方案4】:
保持路由的相同想法。我使用此代码:
app.all('/*', function(req, res, next)
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
next();
);
类似于http://enable-cors.org/server_expressjs.html 示例
【讨论】:
这在 grunt.js 文件中吗? 预检怎么样? 不是@OliverDixon,这是在服务器端 最佳解决方案,无需任何软件包。谢谢!【参考方案5】:做
npm install cors --save
只需将这些行添加到您的请求所在的主文件中(保留在任何路线之前)。
const cors = require('cors');
const express = require('express');
let app = express();
app.use(cors());
app.options('*', cors());
【讨论】:
app.options('*', cors())
// 在其他路由之前包含【参考方案6】:
我做了一个比较完整的适合express或者connect的中间件。它支持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? 某些请求不会包含所有标头。具体来说,浏览器会发送一个 GET 请求,当它没有得到正确的 allow-origin 响应时,就会给 js 一个错误。而对于 POST 请求,首先发送 OPTIONS 请求,并带有 allow-method 标头,然后才会发送实际的 POST 请求。 啊,我明白了。谢谢。如果 req 方法是 'options',你有没有遇到过不放 res.send(200) 的麻烦? 我认为我没有尝试发送其他内容,我想任何其他响应都会导致浏览器拒绝它正在预检的请求。 我不知道该怎么感谢你。非常感谢,伙计?【参考方案7】:做这样的事情:
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();
);
【讨论】:
【参考方案8】:安装 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
【讨论】:
TypeError: Cannot read property 'headers' of undefined
最基本的应用设置。
你确定你有请求对象吗? :)【参考方案9】:
使用 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();
);
【讨论】:
我们需要在哪个文件中添加这些行?【参考方案10】:首先只需在您的项目中安装 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();
);
这对我有用..
【讨论】:
如果你正在做res.header
的东西,你就不需要cors
。 cors
是一个为您处理所有这些的库。删除您的第一行和第三行(也就是 cors
的所有内容),您会发现它仍然有效。
哎呀,我很确定你真正需要的就是这条线res.header("Access-Control-Allow-Origin", "*");
但请记住,这样做会损害您的安全性。 :)【参考方案11】:
前段时间,我遇到了这个问题,所以我这样做是为了在我的 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 不是函数【参考方案12】:这对我有用,因为它在路线内很容易实现,我使用 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);
);
【讨论】:
【参考方案13】:如果你想让它成为特定的控制器,你可以使用:
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。
【讨论】:
【参考方案14】:可以参考下面的代码。来源: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
)
【讨论】:
我看到了很多答案,这有一些很重要的东西,我尝试在其他一些配置之后使用这部分代码,但它没有工作,出于某种原因,我尝试将代码放在后面应用程序const app = express();
并且有效!我认为提及它很重要。【参考方案15】:
最简单的答案就是使用cors package。
const cors = require('cors');
const app = require('express')();
app.use(cors());
这将全面启用 CORS。如果您想学习如何enable CORS without outside modules,您真正需要的只是一些设置'Access-Control-Allow-Origin' header 的Express middleware。这是允许从浏览器到服务器的跨域请求的最低要求。
app.options('*', (req, res) =>
res.set('Access-Control-Allow-Origin', '*');
res.send('ok');
);
app.use((req, res) =>
res.set('Access-Control-Allow-Origin', '*');
);
【讨论】:
【参考方案16】:在我的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();
)
【讨论】:
我应该评论Access-Control-Allow-Origin = *
意味着您将无法将 Cookie 发送到服务器,它们将被 CORS 策略拒绝 - 来源:developer.mozilla.org/en-US/docs/Web/HTTP/…。因此,如果您想使用 cookie,请不要使用它。【参考方案17】:
我最简单的 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', ...)
也可以...
【讨论】:
【参考方案18】:以下对我有用,希望对某人有所帮助!
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? 不确定花生酱.js,对我来说是 express.js。基本上,它应该是您需要 express 应用、初始化它并需要路由文件的文件。【参考方案19】:cors
包是解决 express.js 中 CORS 策略问题的推荐方法,但您还需要确保为app.options
启用它,如下所示:
const cors = require('cors');
// enable cors
app.use(
cors(
origin: true,
optionsSuccessStatus: 200,
credentials: true,
)
);
app.options(
'*',
cors(
origin: true,
optionsSuccessStatus: 200,
credentials: true,
)
);
【讨论】:
【参考方案20】:在typescript中,如果要使用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);
【讨论】:
"如果服务器是用 typescript 编写的" — 不是。问题说它是用 CoffeeScript 编写的。 @Quentin 我只是想在打字机中展示一个替代方案,希望这可以帮助某人。【参考方案21】:使用 Express 中间件非常适合我。如果您已经在使用 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);
);
Reference
【讨论】:
【参考方案22】:在你的主 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();
);
这应该可以解决您的问题
【讨论】:
【参考方案23】:使用CORS
包。并把这个参数:
cors(credentials: true, origin: true, exposedHeaders: '*')
【讨论】:
我希望让我的auth-token
标头在响应标头中可见的确切答案【参考方案24】:
我发现使用 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'));
);
【讨论】:
【参考方案25】:这与 Pat 的回答类似,不同之处在于我使用 res.sendStatus(200); 完成。而不是下一个();
代码将捕获所有方法类型为 OPTIONS 的请求并发送回 access-control-headers。
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。
【讨论】:
【参考方案26】:最简单的方法是在项目中安装 cors 模块:
npm i --save cors
然后在您的服务器文件中使用以下命令将其导入:
import cors from 'cors';
然后像这样简单地将它用作中间件:
app.use(cors());
希望这会有所帮助!
【讨论】:
【参考方案27】:您可以使用 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();
);
【讨论】:
【参考方案28】:简单难:
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))
)
【讨论】:
【参考方案29】:我们可以避免 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");
);
【讨论】:
这不能回答所提出的问题【参考方案30】:我对我的网络应用程序使用了以下步骤,并且取得了成功:
将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.
【讨论】:
以上是关于为啥向 OPTIONS 路由添加 CORS 标头不允许浏览器访问我的 API?的主要内容,如果未能解决你的问题,请参考以下文章