1. 什么时候会发送OPTIONS请求
在 Web 开发中,OPTIONS
请求是一种 HTTP 请求方法,通常与 CORS(跨源资源共享,Cross-Origin Resource Sharing) 机制相关。它主要用于预检(preflight)请求,以检查服务器是否允许特定的跨域请求。以下是详细说明:
1.1. 发送OPTIONS请求条件
1.1.1. 跨域请求且不是简单请求
当发起的请求是跨域的(即请求的目标域名与当前页面域名不同),并且不满足"简单请求"的条件时,浏览器会先发送一个 OPTIONS
请求作为预检请求,以确认服务器是否允许该跨域操作。
简单请求的条件(必须同时满足):
- 请求方法是以下之一:
-
GET
HEAD
POST
- 请求头仅包含以下"安全"的头:
-
Accept
Accept-Language
Content-Language
Content-Type
(且值限于application/x-www-form-urlencoded
、multipart/form-data
或text/plain
)
- 不使用自定义头(如
X-Custom-Header
)。 - 不使用
ReadableStream
等复杂数据。
触发预检的例子:
- 使用
PUT
、DELETE
等非简单方法。 - 设置了自定义请求头(如
Authorization
、X-API-Key
)。 Content-Type
是application/json
。
示例:
php
fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json', // 非简单 Content-Type
'X-Custom-Header': 'value', // 自定义头
},
body: JSON.stringify({ key: 'value' }),
});
这种请求会触发一个 OPTIONS
预检请求。
1.1.2. 服务器要求预检
即使是简单请求,如果服务器在响应中明确要求客户端进行预检(例如通过特定的 CORS 配置),也可能触发 OPTIONS
请求。不过这种情况较少见,通常由服务器端的特殊策略决定。
1.1.3. 手动发送 OPTIONS 请求
开发者也可以通过代码手动发送 OPTIONS
请求(例如使用 fetch
或 XMLHttpRequest
),但这不是常见用法,通常用于测试服务器支持的 HTTP 方法或其他元信息。
sql
fetch('https://api.example.com', { method: 'OPTIONS' });
1.2. OPTIONS 请求的作用
- 询问服务器支持 :
OPTIONS
请求会携带一些头部(如Access-Control-Request-Method
和Access-Control-Request-Headers
),询问服务器是否允许后续的实际请求。 - 服务器响应 :服务器返回支持的方法、允许的头、域名等信息,通过
Access-Control-Allow-*
头部告诉浏览器是否可以继续。
示例请求:
makefile
OPTIONS /data HTTP/1.1
Host: api.example.com
Origin: http://localhost:3000
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, X-Custom-Header
示例响应:
yaml
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, X-Custom-Header
1.3. 如何避免不必要的 OPTIONS 请求?
- 使用简单请求:
-
- 如果可能,限制方法为
GET
/POST
/HEAD
,避免自定义头,使用简单的Content-Type
。
- 如果可能,限制方法为
- 服务器端优化:
-
- 配置 CORS 允许常用的方法和头部,减少预检频率。
- 使用
Access-Control-Max-Age
缓存预检结果,避免重复发送OPTIONS
。
- 调整请求设计:
-
- 如果业务允许,避免跨域,直接部署到同一域名下。
1.4. 总结
- 触发条件:跨域请求 + 非简单请求(如复杂方法或自定义头)。
- 目的:预检服务器是否允许实际请求,确保安全性。
- 常见场景 :现代前端(如 React、Vue)调用后端 API 时,涉及
application/json
或认证头。
如果你的项目中遇到了意外的 OPTIONS
请求,可以检查请求头和方法,或者告诉我具体场景,我可以帮你分析!
2. 什么是CORS?如何解决?
2.1. 什么是 CORS?
CORS(Cross-Origin Resource Sharing,跨源资源共享)是一种浏览器安全机制,用于限制网页中的脚本(如 JavaScript)从一个源(域名、协议、端口)访问另一个源的资源。它由浏览器强制执行,旨在防止恶意网站未经授权访问其他网站的数据。
2.1.1. 核心概念
- 同源策略(Same-Origin Policy) :
-
- 浏览器的默认安全策略,只允许脚本访问与当前页面同源的资源。
- 同源:协议(http/https)、域名(example.com)、端口(80/443)完全相同。
- 示例:
-
-
http://example.com
和http://example.com/api
是同源。http://example.com
和https://example.com
或http://api.example.com
不同源。
-
- 跨源请求:
-
- 当请求的目标资源与当前页面不同源时,称为跨源请求。
- 例如,从
http://localhost:3000
请求https://api.example.com/data
。
2.1.2. CORS 如何工作
- 当浏览器检测到跨源请求时,会根据请求类型(简单请求或非简单请求)执行不同的处理:
-
- 简单请求:
-
-
- 满足特定条件(例如使用
GET
、POST
,且Content-Type
是application/x-www-form-urlencoded
等)。 - 浏览器直接发送请求,并在请求头中添加
Origin
。 - 服务器通过
Access-Control-Allow-Origin
响应是否允许。
- 满足特定条件(例如使用
-
-
- 非简单请求(需要预检):
-
-
- 使用复杂方法(如
PUT
、DELETE
)或自定义头(如Authorization
)。 - 浏览器先发送
OPTIONS
预检请求,询问服务器是否允许。 - 服务器返回允许的方法、头等信息后,浏览器再发送实际请求。
- 使用复杂方法(如
-
2.1.3. 常见的 CORS 错误
- 浏览器控制台报错:
csharp
Access to fetch at 'https://api.example.com' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
- 原因:服务器未返回适当的 CORS 头部,或者不允许当前源。
2.2. 如何解决 CORS 问题?
解决 CORS 问题通常需要服务器端和客户端的配合,但主要依赖服务器端配置。以下是常见的解决方案:
2.2.1. 1. 服务器端解决(推荐)
服务器需要返回正确的 CORS 头部,允许跨源访问。具体的实现取决于服务器技术栈:
- 设置
Access-Control-Allow-Origin
:
-
- 指定允许访问的源,或者使用
*
表示允许所有源。 - 示例(Node.js + Express):
- 指定允许访问的源,或者使用
javascript
const express = require('express');
const app = express();
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'http://localhost:3000'); // 指定源
// 或 res.header('Access-Control-Allow-Origin', '*'); // 允许所有源
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});
app.get('/data', (req, res) => {
res.json({ message: 'Hello' });
});
app.listen(5000);
- 处理预检请求:
-
- 对于非简单请求,服务器需要响应
OPTIONS
请求。 - 示例(Express):
- 对于非简单请求,服务器需要响应
javascript
app.options('*', (req, res) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.sendStatus(204); // 无内容
});
- 使用现成中间件:
-
- Node.js/Express :使用
cors
中间件。
- Node.js/Express :使用
php
const cors = require('cors');
app.use(cors()); // 默认允许所有源
// 或指定选项
app.use(cors({ origin: 'http://localhost:3000' }));
-
- Spring Boot (Java) :
less
@CrossOrigin(origins = "http://localhost:3000")
@RestController
public class MyController {
@GetMapping("/data")
public String getData() {
return "Hello";
}
}
-
- Nginx:
bash
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
return 204;
}
add_header 'Access-Control-Allow-Origin' '*';
}
2.2.2. 2. 客户端解决(临时或开发环境)
如果无法修改服务器,或者只是开发阶段,可以使用以下方法绕过 CORS 限制:
- 代理服务器:
-
- 在本地开发时,通过代理将跨域请求转发到目标服务器。
- Webpack Dev Server 示例:
css
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'https://api.example.com',
changeOrigin: true,
pathRewrite: { '^/api': '' },
},
},
},
};
请求 http://localhost:3000/api/data
会代理到 https://api.example.com/data
。
- 浏览器插件:
-
- 使用如 "Allow CORS: Access-Control-Allow-Origin" 的 Chrome 扩展,临时禁用浏览器的 CORS 限制(仅限开发测试)。
- JSONP(不推荐):
-
- 利用
<script>
标签绕过 CORS,仅适用于GET
请求,老技术,已被现代方案取代。
- 利用
2.2.3. 3. 生产环境最佳实践
- 避免使用
*
:
-
- 用具体域名替换
Access-Control-Allow-Origin: *
,提高安全性。
- 用具体域名替换
- 支持带凭证的请求:
-
- 如果需要发送 Cookie 或认证头(如
Authorization
),设置:
- 如果需要发送 Cookie 或认证头(如
arduino
res.header('Access-Control-Allow-Origin', 'http://localhost:3000'); // 不能用 *
res.header('Access-Control-Allow-Credentials', 'true');
客户端也需设置 withCredentials
:
php
fetch('https://api.example.com', { credentials: 'include' });
- 缓存预检请求:
-
- 添加
Access-Control-Max-Age
头部,避免重复发送OPTIONS
。
- 添加
arduino
res.header('Access-Control-Max-Age', '86400'); // 缓存一天
2.3. 总结
- CORS 是什么:浏览器限制跨源请求的安全机制。
- 解决方法:
-
- 服务器端 :配置 CORS 头部(如
Access-Control-Allow-Origin
),推荐使用中间件。 - 客户端:开发时用代理,生产环境尽量避免绕过。
- 服务器端 :配置 CORS 头部(如
- 关键点:根据请求类型(简单/非简单)和业务需求调整配置。