关于预检请求

跨域资源共享-CORS

在本地电脑双击打开html文件,浏览器地址栏上的url通常是这样的:file:///D:/xxx/index.html

以 file:///文件路径 这种打开文件的方式,叫做本地文件协议 ,它是浏览器用来访问并打开位于用户计算机(本地磁盘)上的文件的协议。

通过 本地文件协议 打开的页面,其 同源策略 会将每个文件甚至每个目录都视为一个独立的、互不信任的"源"。这导致向任何 HTTP 地址发送请求都会被视为 跨源请求 ,而浏览器默认会禁止这种请求,阻止一个"源"的文档或脚本与另一个"源"的资源进行交互,除非对方明确允许。

总而言之,服务器端几乎无法为 file: 协议来源的请求正确配置 CORS

当你尝试在 file: 协议页面中使用 fetchXMLHttpRequest 发送 HTTP 请求时,浏览器控制台会抛出类似以下的错误:

  • Chrome/Edge: Access to fetch at 'http://example.com/' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
  • Firefox: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://example.com/. (Reason: CORS header 'Access-Control-Allow-Origin' missing).

这个错误明确指出了请求是从源 'null'(即 file: 协议)发起的,并且被 CORS 策略阻止了。

所以,我们需要在本地起一个服务,去做跨域的测试。

做跨域测试前,需要了解以下的内容:

预检请求

什么情况下会触发预检请求?

预检请求的成功或者失败是服务端的配置决定的吗?分别是什么配置?

预检请求的作用,是浏览器问服务端,这个请求是否允许访问,如果服务端允许,则预检请求发送成功,然后浏览器才会正式发送第二次请求,对于复杂请求来说。如果预检请求失败了,说明服务端禁止这个请求

如果前端和后端都部署在同一台服务器上,也就是说前后端的地址的域名、端口完全一样,这个时候是否会发生预检请求? 预检请求的触发条件与请求的"源" 无关,而是与请求本身的特性有关。无论是否同源,只要你的请求满足以下任一条件,浏览器就会先发送预检请求:

  1. 使用了非简单方法 (Non-simple Methods):

    • 简单方法GET, HEAD, POST
    • 非简单方法PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH,以及任何自定义方法(如 UPDATE)。
  2. 设置了非简单首部 (Non-simple Headers):

    • 简单首部Accept, Accept-Language, Content-Language, Content-Type (值有限制,见下一条), DPR, Downlink, Save-Data, Viewport-Width, Width
    • 非简单首部 :任何自定义首部(如 X-Requested-With, Authorization(在某些情况下)),以及 Content-Type 的值不属于简单值。
  3. Content-Type 的值不是以下三种之一:

js 复制代码
    -   `application/x-www-form-urlencoded`
    -   `multipart/form-data`
    -   `text/plain`

如果你使用 application/jsontext/xml一定会触发预检

"跨域问题"本质上是浏览器和服务器之间的权限协商问题,而不是前端代码本身的问题。 解决跨域问题的关键永远在于后端服务器的正确配置

在正常情况下,简单请求绝对不会触发预检请求 。而是会直接发送 这个真正的请求(如 GET, POST)到服务器。

测试代码

js 复制代码
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());

app.all('*', (req, res, next) => {
    res.header('Access-Control-Allow-Origin', 'http://127.0.0.1:5500');
    res.header('Access-Control-Allow-Headers', 'Authorization,Content-Type');
    res.header('Access-Control-Allow-Methods', 'GET,POST,PUT');
    next();
})
app.put('/preflight-test', (req, res) => {
    res.json({
        msg: 'preflight-test'
    })
})

app.listen(8080, () => {
    console.log('Server is running on PORT 8080.');
})

复杂请求测试

以下是用vscode插件Live Server启动源为http://127.0.0.1:5500的本地服务器打开的html测试文件

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        fetch('http://localhost:8080/preflight-test', {
            method: 'put'
        });
    </script>
</body>

</html>

可以看到,有两个一模一样的请求。但一个是OPTIONS请求,一个是PUT请求。

修改请求头

js 复制代码
fetch('http://localhost:8080/preflight-test', {
    method: 'PUT',
    headers: {
        'X-Custom-Header': 'test-value'
    }
});

可以发现,虽然源匹配上了,但是还是发生了cors的报错。请求头不对也会导致跨域失败问题。 即使配置了允许源(Origin),但如果请求头(Headers)不匹配,同样会导致跨域预检请求失败。 Origin 只是跨域访问的"第一道关卡"。通过了它,只意味着浏览器愿意把你的请求"派送"到服务器。而请求的"具体内容"(方法、头信息)是否被接受,还需要由服务器返回的 Allow-MethodsAllow-Headers 来决定。

CORS失败 ≠ 连接失败 。它特指浏览器在收到了服务器响应 后,根据响应头中的CORS规则进行校验,发现权限不足,从而主动阻止前端JavaScript代码访问响应结果。

因此,OriginMethodsHeaders 三者是且(AND) 的关系,必须全部满足,请求才能成功。

相关推荐
江上月5132 分钟前
JMeter中级指南:从数据提取到断言校验全流程掌握
java·前端·数据库
代码猎人4 分钟前
forEach和map方法有哪些区别
前端
恋猫de小郭5 分钟前
Google DeepMind :RAG 已死,无限上下文是伪命题?RLM 如何用“代码思维”终结 AI 的记忆焦虑
前端·flutter·ai编程
m0_4711996313 分钟前
【小程序】订单数据缓存 以及针对海量库存数据的 懒加载+数据分片 的具体实现方式
前端·vue.js·小程序
编程大师哥14 分钟前
Java web
java·开发语言·前端
A小码哥15 分钟前
Vibe Coding 提示词优化的四个实战策略
前端
Murrays16 分钟前
【React】01 初识 React
前端·javascript·react.js
大喜xi19 分钟前
ReactNative 使用百分比宽度时,aspectRatio 在某些情况下无法正确推断出高度,导致图片高度为 0,从而无法显示
前端
helloCat19 分钟前
你的前端代码应该怎么写
前端·javascript·架构
电商API_1800790524720 分钟前
大麦网API实战指南:关键字搜索与详情数据获取全解析
java·大数据·前端·人工智能·spring·网络爬虫