ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

3个真实案例教你搞定跨域遭到拦截的新手避坑指南

3个真实案例教你搞定跨域遭到拦截的新手避坑指南 3个真实案例教你搞定跨域遭到拦截的新手避坑指南 配置环境就卡半天,明明本地跑得好好的,一部署到服务器或者换个浏览器就报错,这种“遭到”拦截的痛苦,谁懂? 很多新手在调试前端请求时,最容易陷入的误区就是死磕代码逻辑,却忽略了浏览器安全机制的底层规则。今天咱们不整虚的,直接拆解 HTTP 预检请求(Preflight Request)这个让无数人头疼的“拦路虎”。 核心痛点直击:为什么你的 fetch 或 axios 请求在控制台里显示 CORS error?为什么加上了 Access-Control-Allow-Origin 还是被浏览器阻断? 这就是一场关于“浏览器安全策略”与“后端响应头”的博弈。搞不懂这个,你的前端项目永远在“遭到”各种奇奇怪怪的网络错误中挣扎。 1. 一句话原理:浏览器是守门员,不是传声筒 很多开发者误以为 CORS(跨域资源共享)是后端的事,其实浏览器才是第一道关卡。 当你的前端页面(比如 https://a.com)试图去请求另一个源(比如 https://b.com)的数据时,浏览器会先检查 b.com 返回的响应头里,有没有明确写着“我允许 a.com 来访问我”。 如果没写,或者写错了,浏览器就会直接拦截这个响应,根本不会把数据交给你的 JavaScript 代码处理。这时候,你在控制台看到的错误,不是网络断了,而是浏览器故意不让你看。 这就是“遭到”拦截的本质:同源策略(Same-Origin Policy)的强制执行。关键记忆点:CORS 不是前端技术,是浏览器安全机制 + 后端配置 的共同产物。前端能做的只是“发起请求”和“监听错误”,真正决定“放行”还是“拦截”的,是后端返回的 HTTP 响应头。2. 类比解释:就像寄快递,得看收件人是否签收 想象一下,你(前端代码)想给住在隔壁小区的人(后端服务器)寄个重要文件。简单请求:如果你只是寄个明信片(GET/POST 简单内容),邮局(浏览器)会直接送过去。但如果收件人(后端)在门口贴了告示:“只收本公司快递”,那邮局就会拒收,并告诉你“遭到拒收”。 复杂请求(预检):如果你寄的是个大箱子,里面装了敏感物品(比如带自定义 Header 的 POST 请求),邮局会先派个快递员(OPTIONS 预检请求)去问问收件人:“我要送这个大箱子,你收吗?”收件人(后端)必须回复:“收,我允许 a.com 的快递员送。” 只有收到这个明确的“收”的回复,邮局才会真正派车把大箱子送过去。 如果收件人没回复,或者回复“不收”,邮局就直接终止流程,箱子根本没发出去。新手避坑重点:很多新手只盯着“发箱子”(实际请求),却忽略了“问收件人”(预检请求)这一步。如果后端没配置好 OPTIONS 方法的响应,整个流程就会在预检阶段就“遭到”阻断。 3. 源码与伪代码:后端到底该怎么配? 光讲理论没用,直接上代码。这里以 Node.js (Express) 和 Spring Boot 为例,展示如何正确配置 CORS,避免“遭到”浏览器拦截。 场景一:Node.js (Express) 很多新手喜欢手动写中间件,结果漏掉了 OPTIONS 方法,导致预检请求失败。 const express = require('express'); const app = express();// 错误示范:只处理了 GET/POST,没处理 OPTIONS // app.use((req, res, next) = { // res.header('Access-Control-Allow-Origin', 'http://localhost:3000'); // res.header('Access-Control-Allow-Methods', 'GET, POST'); // next(); // });// 正确示范:使用 cors 中间件,或者手动完整配置 const cors = require('cors');// 方式1:使用 cors 包(推荐,省心) app.use(cors({origin: 'http://localhost:3000', // 指定允许的前端地址methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], // 必须包含 OPTIONSallowedHeaders: ['Content-Type', 'Authorization'] // 允许前端自定义的 Header }));// 方式2:手动配置(用于理解原理) app.use((req, res, next) = {// 1. 允许跨域来源res.header('Access-Control-Allow-Origin', 'http://localhost:3000');// 2. 允许的请求方法,必须包含 OPTIONS(预检请求)res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');// 3. 允许的 Header,如果前端发了 Authorization,这里必须允许res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');// 4. 如果请求类型是 OPTIONS,直接返回 200,不要进入后续业务逻辑if (req.method === 'OPTIONS') {return res.sendStatus(200);}next(); });app.get('/api/data', (req, res) = {res.json({ message: '数据拿到了,没有遭到拦截' }); });app.listen(8080);逐行解析:Access-Control-Allow-Origin: 告诉浏览器,谁可以来访问我。如果是 *,表示所有人都可以(但携带凭证时不能用 *)。 Access-Control-Allow-Methods: 告诉浏览器,我支持哪些 HTTP 方法。切记:必须包含 OPTIONS,否则预检请求会 404 或 405,导致“遭到”拦截。 Access-Control-Allow-Headers: 如果前端请求里带了自定义 Header(如 token),这里必须声明允许,否则浏览器也会拦截。场景二:Java (Spring Boot) Java 后端的新手更爱用 @CrossOrigin 注解,但很容易漏掉配置范围。 import org.springframework.web.bind.annotation.*;@RestController @RequestMapping(/api) public class UserController {// 错误示范:默认只允许当前类的方法,且可能未配置预检// @CrossOrigin// @GetMapping(/user)// public String getUser() { ... }// 正确示范:明确指定允许的来源、方法和 Header@CrossOrigin(origins = http://localhost:3000, // 前端地址methods = {RequestMethod.GET, RequestMethod.POST, RequestMethod.OPTIONS}, // 必须包含 OPTIONSallowedHeaders = {Content-Type, Authorization})@PostMapping(/user)public String createUser(@RequestBody User user) {return 创建成功,未遭到CORS拦截;}// 处理预检请求@CrossOrigin(origins = http://localhost:3000)@RequestMapping(method = RequestMethod.OPTIONS)public void handlePreflight() {// 空方法即可,Spring 会自动设置响应头} }新手避坑:在 Spring Boot 中,如果使用了 @CrossOrigin,建议全局配置 WebMvcConfigurer,而不是每个接口都加注解,容易遗漏。 4. 流程描述:一次成功的跨域请求发生了什么? 我们用文字流程来还原浏览器视角的“遭到”与“放行”过程。 场景:前端 http://localhost:3000 请求后端 http://localhost:8080/api/data 步骤 1:前端发起请求 JavaScript 代码执行 fetch('http://localhost:8080/api/data', { method: 'POST', headers: { 'Content-Type': 'application/json' } })。 步骤 2:浏览器判断请求类型方法:POST Header:Content-Type: application/json(非简单类型,属于复杂请求) 源不同:localhost:3000 != localhost:8080 结论:需要发送预检请求(Preflight Request)。步骤 3:浏览器发送 OPTIONS 请求 浏览器自动发送: OPTIONS /api/data HTTP/1.1 Host: localhost:8080 Origin: http://localhost:3000 Access-Control-Request-Method: POST Access-Control-Request-Headers: content-type步骤 4:后端处理 OPTIONS 后端收到 OPTIONS 请求,检查配置:是否允许 http://localhost:3000? - 是 是否允许 POST 方法? - 是 是否允许 Content-Type Header? - 是 响应:HTTP/1.1 200 OK Access-Control-Allow-Origin: http://localhost:3000 Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization Access-Control-Max-Age: 3600步骤 5:浏览器校验预检响应Access-Control-Allow-Origin 是否匹配请求中的 Origin? - 匹配 Access-Control-Allow-Methods 是否包含 POST? - 包含 Access-Control-Allow-Headers 是否包含 Content-Type? - 包含 结论:预检通过,未遭到拦截。步骤 6:浏览器发送实际请求 POST /api/data HTTP/1.1 Host: localhost:8080 Content-Type: application/json { name: test }步骤 7:后端处理实际请求 后端返回 JSON 数据,并再次附带 CORS 头(虽然预检通过了,实际响应也需要有 Access-Control-Allow-Origin,否则浏览器还是会拦截响应体)。 步骤 8:浏览器交付数据 JavaScript 的 then 回调函数接收到数据。 如果任何一步“遭到”失败?后端返回 404/405 给 OPTIONS - 预检失败,实际请求根本不发。 后端 OPTIONS 响应头缺失 - 预检失败。 实际请求响应头缺失 Access-Control-Allow-Origin - 预检通过,但响应被浏览器拦截,前端代码拿到的是 undefined 或错误,控制台报 CORS Error。5. 实战验证与进阶避坑 常见“遭到”拦截的三大坑Access-Control-Allow-Origin 设为 * 但携带了 Cookie现象:前端用了 withCredentials: true,后端返回 Access-Control-Allow-Origin: *。 结果:浏览器直接报错,遭到拦截。 解决:当携带凭证时,Access-Control-Allow-Origin 必须指定具体域名,不能是 *。同时后端需设置 Access-Control-Allow-Credentials: true。忽略 Access-Control-Max-Age现象:每次页面刷新或请求,都会先发一个 OPTIONS 预检请求,增加延迟。 解决:设置 Access-Control-Max-Age: 3600,告诉浏览器缓存预检结果 1 小时,期间不再发送 OPTIONS 请求。Nginx 反向代理未配置 CORS 头现象:本地开发正常,上线后遭到拦截。 原因:生产环境通常通过 Nginx 代理,如果 Nginx 层没有配置 CORS 头,或者后端没配,请求在 Nginx 层就被“截胡”了。 解决:在 Nginx 配置中加上:location / {add_header Access-Control-Allow-Origin $http_origin;add_header Access-Control-Allow-Credentials true;add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS';add_header Access-Control-Allow-Headers 'Content-Type, Authorization';if ($request_method = 'OPTIONS') {return 200;}proxy_pass http://backend; }如何快速定位是“预检失败”还是“实际请求失败”? 打开浏览器开发者工具(F12)- Network 面板:看有没有 OPTIONS 请求。有,且状态码不是 200/204 - 预检失败,检查后端 OPTIONS 处理逻辑。 有,且状态码是 200,但 POST/GET 请求显示红色错误 - 实际请求响应头缺失,检查后端实际接口的响应头。看 Console 面板的错误信息。Access to XMLHttpRequest at '...' from origin '...' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. - 典型的响应头缺失。 CORS policy: Request header field 'xxx' is not allowed by Access-Control-Allow-Headers in preflight response. - 预检时 Header 不被允许。权威参考 关于 CORS 的规范细节,建议查阅 MDN Web Docs 中的 CORS - Cross-Origin Resource Sharing 章节。MDN 详细列出了简单请求与复杂请求的区别,以及浏览器处理流程的官方定义,是排查此类问题的权威依据。 新手避坑总结:CORS 是浏览器机制,不是前端 bug。 复杂请求必须处理 OPTIONS。 携带凭证时不能用 *。 生产环境注意 Nginx 配置。互动时间: 你公司项目里是怎么处理跨域问题的?是统一在网关层配置,还是每个微服务单独配?有没有遇到过因为 Access-Control-Max-Age 没设导致接口响应变慢的情况?欢迎在评论区分享你的踩坑经历和解决方案,咱们一起避坑!
返回列表