基于本文回答

播面 播面

刷题像听歌,多听自然懂
0
评论

简单请求和复杂请求(预检请求 Preflight)的区别是什么?

知识点图片

在浏览器处理 跨域资源共享 (CORS) 时,将请求分为两大类:简单请求 (Simple Requests)复杂请求 (Complex Requests)

核心区别在于:是否会触发“预检请求”(Preflight Request,即 OPTIONS 请求)。

以下是详细的对比和解释:


1. 简单请求 (Simple Request)

简单请求是指那些不会触发 CORS 预检的请求。浏览器认为这类请求是“安全的”,因为它们类似于传统的 HTML 表单提交(在 CORS 出现之前就已经存在)。

触发条件(必须全部满足)

如果一个请求满足以下 所有 条件,它就是简单请求:

  1. HTTP 方法 必须是以下三种之一:
    • GET
    • HEAD
    • POST
  2. HTTP 头部 (Headers) 不得包含自定义头部,只能包含以下“CORS 安全头部”:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type (有限制,见下条)
    • Last-Event-ID (较少见)
  3. Content-Type 的值仅限于以下三者之一:
    • text/plain
    • multipart/form-data
    • application/x-www-form-urlencoded

请求流程

  1. 浏览器直接发出跨域请求。
  2. 请求头中携带 Origin 字段。
  3. 服务器返回响应,如果响应头包含正确的 Access-Control-Allow-Origin,浏览器就接收响应;否则报错。

2. 复杂请求 / 需预检的请求 (Preflighted Request)

凡是不满足“简单请求”条件的,都是复杂请求。浏览器担心这类请求可能会对服务器数据产生副作用(如删除、修改),所以要求先询问服务器是否允许。

常见触发场景

只要满足 任意 一个条件,就是复杂请求:

  1. 使用了非简单的方法:如 PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH
  2. 设置了自定义头部:例如 Authorization (Token), X-Custom-Header 等。
  3. Content-Type 不在允许范围内:最常见的就是 application/json

请求流程(关键区别)

复杂请求会产生 两次 HTTP 交互:

第一步:预检请求 (Preflight / OPTIONS)

  • 浏览器自动先发送一个 OPTIONS 方法的请求。
  • 询问服务器:“我可以使用 DELETE 方法吗?我可以带 Authorization 头吗?”
  • 请求头包含:
    • Access-Control-Request-Method: 实际请求的方法。
    • Access-Control-Request-Headers: 实际请求的自定义头。

第二步:服务器确认

  • 服务器检查配置,如果允许,返回状态码 204200
  • 响应头包含:
    • Access-Control-Allow-Methods: 允许的方法。
    • Access-Control-Allow-Headers: 允许的头。
    • Access-Control-Max-Age: 预检结果的缓存时间(在此时间内无需再次预检)。

第三步:实际请求

  • 只有预检请求成功后,浏览器才会发出真正的业务请求(如 POST JSON 数据)。

3. 总结对比表

特性 简单请求 (Simple) 复杂请求 (Preflighted)
HTTP 请求次数 1次 (直接发送) 2次 (先 OPTIONS,再发送实际请求)
HTTP 方法 仅限 GET, POST, HEAD PUT, DELETE, PATCH 等,或 GET/POST 带有特殊配置
Content-Type form-data, urlencoded, text/plain application/json, application/xml 等
自定义 Header 不允许 允许 (如 Authorization)
典型场景 传统的表单提交、加载图片/脚本 RESTful API 调用 (发送 JSON, 带 Token, 使用 PUT/DELETE)
性能影响 较快,低延迟 较慢,因为多了一次 OPTIONS 网络往返

4. 开发者常遇到的坑

为什么我发送 POST 请求也会触发 OPTIONS 预检?

这是面试或开发中最常见的问题。

  • 情况 A (简单请求): 你用 POST 发送表单数据 (application/x-www-form-urlencoded),不会触发预检。
  • 情况 B (复杂请求): 你用 Axios 或 Fetch 发送 JSON 数据 (application/json),或者你在 Header 里加了 Authorization: Bearer token。这触发预检。

5. 如何优化复杂请求?

由于复杂请求多一次网络往返,会增加延迟。可以通过设置 Access-Control-Max-Age 来优化。

  • 服务器在 OPTIONS 响应中设置该字段(单位为秒,例如 86400)。
  • 浏览器在有效期内,对完全相同的跨域请求不再发送 OPTIONS,直接发送实际请求。
00:00
00:00