HTTP请求预检请求是什么
你在写前端代码时,可能遇到过这样的情况:明明只发了一次请求,但在浏览器的开发者工具里却看到发了两次。第一次是 OPTIONS,第二次才是你真正想发的 POST 或 PUT。这个多出来的 OPTIONS 请求,就是“预检请求”(Preflight Request)。
什么时候会触发预检
预检不是每次请求都来,它只在跨域(CORS)且满足“非简单请求”条件时才会出现。简单说,如果你的请求带了自定义头、用了除 GET、POST、HEAD 以外的方法,或者 Content-Type 是 application/json 之外的类型(比如 text/plain),浏览器就会先发个 OPTIONS 探探路。
比如你用 fetch 发个带 token 的请求:
fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer xyz'
},
body: JSON.stringify({ name: 'test' })
});虽然看着就一次,但因为加了 Authorization 头,浏览器觉得这事儿得先问问服务器同不同意,于是先发个 OPTIONS 请求。
预检请求长什么样
那个 OPTIONS 请求里会带上几个关键头信息:
Access-Control-Request-Method:告诉你接下来要用什么方法,比如POSTAccess-Control-Request-Headers:列出所有自定义头,比如Authorization, Content-Type
服务器收到后,得判断这些方法和头是不是允许的。如果允许,就返回 200 并带上 Access-Control-Allow-Methods 和 Access-Control-Allow-Headers。浏览器一看,哦,能干,才继续发真正的请求。
怎么避免频繁预检
预检虽然安全,但多一次网络往返,总归有开销。如果你控制得了后端,可以加个响应头让浏览器缓存预检结果:
Access-Control-Max-Age: 86400这表示这次预检结果可以缓存一天,期间同样的请求就不用再预检了。注意,不同浏览器对最大缓存时间有限制,Chrome 是 24 小时,别设太离谱。
还有一点,尽量用简单请求能绕开预检。比如把 Content-Type 改成 application/x-www-form-urlencoded,或者避免加自定义头,就能少一次请求。
预检请求就像进小区前保安先打电话确认业主是否在家。虽然烦了点,但为了安全,也挺必要。