什么是预检请求?
想象你要去一个从未去过的国家旅行。在真正踏上旅程之前,你可能会先打电话给那个国家的海关,问问你是否可以入境,需要带什么证件,是否需要签证等。这个打电话询问的过程,就像是预检请求。
在网络世界中,当你的网页想要访问另一个网站的数据(这就是所谓的跨源请求)时,浏览器有时会先发送一个”预检请求”,就是在问那个网站:”嘿,我可以用这种方式访问你的数据吗?”
预检请求是浏览器在发送实际的跨源请求之前,先向服务器发送的一个特殊的 OPTIONS 请求,用于检查实际请求是否安全可接受。
为什么会有预检请求?
- 安全考虑:就像国家要控制谁可以入境一样,网站也需要控制谁可以访问它们的数据。
- 保护隐私:预检请求不会包含实际的数据,就像你打电话询问入境要求时,不需要提供你的全部个人信息。
- 避免麻烦:如果直接发送可能不被允许的请求,就像直接飞到一个国家却被拒绝入境,这样会很麻烦。
预检请求的存在是为了增加浏览器和服务器之间交互的安全性,防止恶意的跨域请求滥用用户身份信息或对服务器资源造成威胁。这是一种保护机制,确保只有经过服务器许可的跨域请求才会被执行。
哪些情况会发起预检请求?
- 同源请求:不需要预检,无论请求多么特殊。
- 跨源请求:可能需要预检,取决于请求的具体特征。
对于同源请求,即使请求很特殊,也不会触发预检请求。这是因为同源策略(Same-Origin Policy)本身就是一种安全机制,浏览器默认信任来自同一源的请求。
比如,
- 使用特殊的”旅行方式”:如果你想骑大象入境(比喻使用 PUT, DELETE 等特殊的 HTTP 方法),而不是普通的步行(GET, POST),海关可能会要求你提前通知。
- 携带特殊物品:如果你想带一些特殊的物品入境(比如使用自定义的 HTTP 头部,例如,自定义的 X-Custom-Header 或 Authorization ),也需要提前询问。
- 特殊的行李包装:如果你的行李包装方式很特别(比如,如果 Content-Type 请求头的值不是以下三种之一,
application/x-www-form-urlencoded
,multipart/form-data
,text/plain
,比如发送 application/json 数据),也可能需要预先检查。
举个具体的例子:
假设你有一个网页游戏,需要向另一个网站的服务器发送玩家的高分数据。
fetch('https://other-website.com/api/scores', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Player-Id': '12345',
},
body: JSON.stringify({ score: 1000 }),
});
这个请求会触发预检,因为:
- 它使用了自定义头部(X-Player-Id)
- 它发送的是 JSON 数据
浏览器会先发送一个 OPTIONS 请求(预检请求),询问服务器是否允许这样的请求。如果服务器答应了,真正的 POST 请求才会被发送。