Web - 为什么POST请求经常发送两次?
大多数前端程序员,在通过浏览器F12的调试工具调试网络请求时,可能都会有一个发现,在进行POST请求时明明代码里只请求了一次,为什么network里发送了两次呢,难道代码出bug了?带着疑问点开第一个请求才发现,原来第一个是OPTIONS类型的请求,第二个才是代码里写的POST类型的请求。为什么POST请求之前默认伴随着一个OPTIONS请求呢?
背景
为什么会有这两次请求?逐步探究CORS预检的原因和机制。
什么是CORS(跨源资源共享)
CORS是一种浏览器的安全策略,用于控制一个源(domain、protocol、port的组合)的Web页面是否可以请求另一个源的资源。CORS通过在服务器响应头中添加特定的字段,告诉浏览器是否允许来自其他源的请求。
为什么POST请求需要CORS预检
OPTIONS 方法:用于查询服务器针对特定资源所支持的 HTTP 请求方式,即询问客户端可以以哪些方式来请求相应的资源, 同时使用 OPTIONS 也可以用来测试服务器的性能。(用于查询服务器支持的请求方法)
POST请求通常用于向服务器提交数据,但由于安全性考虑,浏览器会限制跨域POST请求。在实际发送POST请求之前,浏览器会发送一个OPTIONS请求,以便确认目标服务器是否允许实际的POST请求。
GET请求一定不需要CORS预检吗
CORS预检是一种安全机制,用于控制跨域请求的访问权限。对于简单请求(Simple Requests),包括使用GET、HEAD、POST其中一种方法,且只使用了以下几种简单请求头(Simple Request Headers)的请求,浏览器会自动处理CORS,无需进行预检:
- Accept
- Accept-Language
- Content-Language
- Content-Type(仅限于 application/x-www-form-urlencoded、multipart/form-data、text/plain)
因此,GET请求通常不会触发CORS预检。只有当请求是跨域的、使用了非简单请求头、或者使用了不支持的HTTP方法时,才会触发CORS预检。对于非简单请求,浏览器会在实际请求之前发送一个OPTIONS请求,用来确认服务器是否支持跨域请求。而对于简单请求,浏览器会直接发送实际的GET请求,不需要进行预检。
CORS预检的过程
1)浏览器发送OPTIONS请求:
当浏览器发现一个跨域的POST请求时,它首先发送一个OPTIONS请求到目标服务器,这是CORS预检的开始。
2)服务器响应CORS头信息:
服务器接收到OPTIONS请求后,检查请求中的信息,并返回响应。响应中包含了CORS头信息,其中包括允许的HTTP方法、允许的请求头等。如果服务器返回的CORS头信息允许实际的POST请求,浏览器才会继续发送实际的POST请求。
服务端示例代码(NodeJS)
在服务器端,需要配置CORS,以允许来自特定源的POST请求。以下是NodeJS Express框架的示例:
const express = require('express');
const app = express();
// 配置CORS,允许所有源的POST请求
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*'); // 允许所有源
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); // 允许的HTTP方法
res.header('Access-Control-Allow-Headers', 'Content-Type'); // 允许的请求头
next();
});
// 实际的POST请求处理
app.post('/api/data', (req, res) => {
// 处理POST请求的逻辑
res.send('POST请求成功!');
});
app.listen(3000, () => {
console.log('服务器启动在端口 3000');
});
在上述代码中,通过配置Access-Control-Allow-Origin、Access-Control-Allow-Methods和Access-Control-Allow-Headers等响应头信息,服务器明确指定了允许的源、HTTP方法和请求头。
总结
POST请求发送两次的现象是因为浏览器在执行跨域的POST请求时,为了确保安全性,会发送一个OPTIONS请求进行CORS预检。服务器的CORS配置决定了是否允许实际的POST请求。理解CORS预检的过程,能够帮助更好地处理跨域请求问题,确保Web应用的安全性和稳定性。