为什么即使我的前端代码只是发出 POST 请求,浏览器也会发送 OPTIONS 请求?
Why does the browser send an OPTIONS request even though my frontend code is just making a POST request?
我的front-end代码:
<form action="" onSubmit={this.search}>
<input type="search" ref={(input) => { this.searchInput = input; }}/>
<button type="submit">搜索</button>
</form>
// search method:
const baseUrl = 'http://localhost:8000/'; // where the Express server runs
search(e) {
e.preventDefault();
let keyword = this.searchInput.value;
if (keyword !== this.state.lastKeyword) {
this.setState({
lastKeyword: keyword
});
fetch(`${baseUrl}search`, {
method: 'POST',
// mode: 'no-cors',
headers: new Headers({
'Content-Type': 'application/json'
}),
// credentials: 'include',
body: JSON.stringify({keyword})
})
}
}
我的 Express.js 服务器代码:
app.all('*', (req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type');
// res.header('Access-Control-Allow-Credentials', true);
res.header('Content-Type', 'application/json; charset=utf-8')
next();
});
当我提交表单时,我收到了两个请求。其中一个是 OPTIONS 请求,另一个是 POST 请求,对它的响应是正确的:
可以看到,Express服务器运行在8000端口,React开发服务器运行在3000端口。localhost:3000
正在请求localhost:8000/search
,localhost:8000
正在请求另一个origin 通过使用 POST 方法。但是,只有第二个请求运行良好。我不知道这是怎么发生的。当然,如果我用 querystring 发出 GET 请求,一切都是正常的。但我也想知道如何使用请求正文进行 POST 提取。
OPTIONS
请求由您的浏览器自动发送,然后它会尝试从您的代码发出 POST
请求。这称为 CORS 预检。
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests 有详细信息。
在这种特定情况下,要点是代码添加的 Content-Type: application/json
请求 header 触发浏览器执行预检 OPTIONS
请求。
因此,该特定预检请求的目的是让浏览器询问服务器,“您是否允许 cross-origin POST
具有 Content-Type
的请求header 其值不是 application/x-www-form-urlencoded
、multipart/form-data
或 text/plain
之一?”
为了让浏览器认为预检成功,服务器必须发回一个 Access-Control-Allow-Headers
header 的响应,其值中包含 Content-Type
。
所以我看到您在 http://localhost:8000/
上的当前服务器代码中有 res.header('Access-Control-Allow-Headers', 'Content-Type')
,如果您要以这种方式手动编码,那么这是设置的正确值。但我认为这不起作用的原因是因为您还没有明确处理 OPTIONS
请求的代码。
要解决这个问题,您可以尝试安装 npm cors
软件包:
npm install cors
…然后做这样的事情:
var express = require('express')
, cors = require('cors')
, app = express();
const corsOptions = {
origin: true,
credentials: true
}
app.options('*', cors(corsOptions)); // preflight OPTIONS; put before other routes
app.listen(80, function(){
console.log('CORS-enabled web server listening on port 80');
});
这将处理 OPTIONS
请求,同时还发回正确的 header 和值。
我最近遇到了类似的问题,使用 FormData 发送我的请求负载,而您不需要发送自定义 headers。 FormData object 会为您处理身份验证凭据,但它使用 SSL 并且只能使用本地主机不提供的 https,因为本地主机使用 http 协议。
检查 form-data npm documentation 以获取有关如何在 Express 应用程序中安装和使用它的详细信息,但 FormData 可随时用于主要浏览器。
这是一个简单的代码示例
npm install --save form-data
`
var FormData = require('form-data');
var http = require('http');
var form = new FormData();
http.request('http://nodejs.org/images/logo.png', function(response) {
form.append('my_field', 'my value');
form.append('my_buffer', new Buffer(10));
form.append('my_logo', response);
});
`
我的front-end代码:
<form action="" onSubmit={this.search}>
<input type="search" ref={(input) => { this.searchInput = input; }}/>
<button type="submit">搜索</button>
</form>
// search method:
const baseUrl = 'http://localhost:8000/'; // where the Express server runs
search(e) {
e.preventDefault();
let keyword = this.searchInput.value;
if (keyword !== this.state.lastKeyword) {
this.setState({
lastKeyword: keyword
});
fetch(`${baseUrl}search`, {
method: 'POST',
// mode: 'no-cors',
headers: new Headers({
'Content-Type': 'application/json'
}),
// credentials: 'include',
body: JSON.stringify({keyword})
})
}
}
我的 Express.js 服务器代码:
app.all('*', (req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type');
// res.header('Access-Control-Allow-Credentials', true);
res.header('Content-Type', 'application/json; charset=utf-8')
next();
});
当我提交表单时,我收到了两个请求。其中一个是 OPTIONS 请求,另一个是 POST 请求,对它的响应是正确的:
可以看到,Express服务器运行在8000端口,React开发服务器运行在3000端口。localhost:3000
正在请求localhost:8000/search
,localhost:8000
正在请求另一个origin 通过使用 POST 方法。但是,只有第二个请求运行良好。我不知道这是怎么发生的。当然,如果我用 querystring 发出 GET 请求,一切都是正常的。但我也想知道如何使用请求正文进行 POST 提取。
OPTIONS
请求由您的浏览器自动发送,然后它会尝试从您的代码发出 POST
请求。这称为 CORS 预检。
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests 有详细信息。
在这种特定情况下,要点是代码添加的 Content-Type: application/json
请求 header 触发浏览器执行预检 OPTIONS
请求。
因此,该特定预检请求的目的是让浏览器询问服务器,“您是否允许 cross-origin POST
具有 Content-Type
的请求header 其值不是 application/x-www-form-urlencoded
、multipart/form-data
或 text/plain
之一?”
为了让浏览器认为预检成功,服务器必须发回一个 Access-Control-Allow-Headers
header 的响应,其值中包含 Content-Type
。
所以我看到您在 http://localhost:8000/
上的当前服务器代码中有 res.header('Access-Control-Allow-Headers', 'Content-Type')
,如果您要以这种方式手动编码,那么这是设置的正确值。但我认为这不起作用的原因是因为您还没有明确处理 OPTIONS
请求的代码。
要解决这个问题,您可以尝试安装 npm cors
软件包:
npm install cors
…然后做这样的事情:
var express = require('express')
, cors = require('cors')
, app = express();
const corsOptions = {
origin: true,
credentials: true
}
app.options('*', cors(corsOptions)); // preflight OPTIONS; put before other routes
app.listen(80, function(){
console.log('CORS-enabled web server listening on port 80');
});
这将处理 OPTIONS
请求,同时还发回正确的 header 和值。
我最近遇到了类似的问题,使用 FormData 发送我的请求负载,而您不需要发送自定义 headers。 FormData object 会为您处理身份验证凭据,但它使用 SSL 并且只能使用本地主机不提供的 https,因为本地主机使用 http 协议。 检查 form-data npm documentation 以获取有关如何在 Express 应用程序中安装和使用它的详细信息,但 FormData 可随时用于主要浏览器。 这是一个简单的代码示例 npm install --save form-data
`
var FormData = require('form-data');
var http = require('http');
var form = new FormData();
http.request('http://nodejs.org/images/logo.png', function(response) {
form.append('my_field', 'my value');
form.append('my_buffer', new Buffer(10));
form.append('my_logo', response);
});
`