跨域问题排查实战:一个困扰两天的线上问题
"老师,我们的新功能上线后接口突然调不通了!"周一早上,实习生小李急匆匆地跑来找我。我打开监控面板,发现生产环境的错误日志突然暴增,全是 CORS 相关的报错。作为技术导师,我立即和小李一起开始排查这个问题。
说实话,跨域问题在本地开发时很常见,但在生产环境突然出现还是第一次。更让人困惑的是,这些接口在上周五还是好好的,周末发版后就出问题了。带着这些疑问,我们开始了一场"破案"之旅。
问题的表象
首先,我让小李演示了一下具体的错误场景。在浏览器控制台里,我们看到了这样的报错:
Access to XMLHttpRequest at 'https://api.example.com/data' from origin 'https://www.example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
"奇怪了,我们的 CORS 配置明明一直都在啊。"小李一脸困惑。我们开始仔细梳理上周的发版内容:
// 上周的发版记录 const deployChanges = { frontend: { 'feat: 新增数据分析页面': { api: 'https://api-analysis.example.com/v1', changes: ['新增数据大屏', '接入实时数据接口', '对接新的认证服务'] } }, backend: { 'refactor: 服务架构调整': { changes: ['网关服务升级', '认证服务重构', '引入服务网格'] } } }
深入排查
通过对比测试环境和生产环境的请求,我们发现了一些线索:
// 测试环境的请求(正常) fetch('https://api-test.example.com/data', { headers: { Authorization: 'Bearer token123', 'Content-Type': 'application/json' } }).then(response => { console.log('响应头:', response.headers) // Access-Control-Allow-Origin: https://www.example.com // Access-Control-Allow-Methods: GET, POST, OPTIONS // Access-Control-Allow-Headers: Content-Type, Authorization }) // 生产环境的请求(异常) fetch('https://api.example.com/data', { headers: { Authorization: 'Bearer token123', 'Content-Type': 'application/json' } }).then(response => { console.log('响应头:', response.headers) // 缺少 CORS 相关的响应头 })
通过进一步分析,我们发现问题出在新引入的服务网格配置上:
# 原来的网关配置 apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: api-gateway spec: hosts: - 'api.example.com' gateways: - api-gateway http: - route: - destination: host: api-service port: number: 80 # 这里缺少了 CORS 策略配置
问题的根源
原来是这样!在服务架构调整时,我们将原来网关层的 CORS 配置迁移到了服务网格,但是漏掉了一些细节:
- 预检请求(OPTIONS)没有正确配置
- 多级域名的跨域配置缺失
- 认证头信息没有加入允许列表
解决方案
知道问题后,解决方案就比较清晰了:
# 修复后的配置 apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: api-gateway spec: hosts: - 'api.example.com' - 'api-analysis.example.com' gateways: - api-gateway http: - corsPolicy: allowOrigins: - exact: 'https://www.example.com' - regex: 'https://*.example.com' allowMethods: - GET - POST - OPTIONS allowHeaders: - Authorization - Content-Type maxAge: '24h' route: - destination: host: api-service port: number: 80
同时,我们在前端也增加了错误处理机制:
// utils/request.ts class APIClient { private async request(url: string, options: RequestOptions) { try { const response = await fetch(url, { ...options, headers: { ...options.headers, 'Content-Type': 'application/json' } }) if (!response.ok) { // 处理 HTTP 错误 throw new HTTPError(response.status, response.statusText) } return await response.json() } catch (error) { if (error instanceof HTTPError) { // 处理 HTTP 错误 if (error.status === 0) { console.error('可能是跨域问题:', error) // 显示友好的错误提示 notification.error({ message: '网络请求失败', description: '请检查网络连接或联系技术支持' }) } } throw error } } // 提供重试机制 async requestWithRetry(url: string, options: RequestOptions, retries = 3) { for (let i = 0; i < retries; i++) { try { return await this.request(url, options) } catch (error) { if (i === retries - 1) throw error // 延迟重试 await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i))) } } } }
预防措施
为了防止类似问题再次发生,我们建立了一套完整的测试机制:
// tests/cors.spec.ts describe('CORS Configuration Tests', () => { const origins = ['https://www.example.com', 'https://admin.example.com', 'https://data.example.com'] const endpoints = ['/api/v1/data', '/api/v1/analysis', '/api/v1/auth'] origins.forEach(origin => { endpoints.forEach(endpoint => { it(`should allow CORS from ${origin} to ${endpoint}`, async () => { const response = await fetch(`https://api.example.com${endpoint}`, { method: 'OPTIONS', headers: { Origin: origin, 'Access-Control-Request-Method': 'POST', 'Access-Control-Request-Headers': 'Content-Type,Authorization' } }) expect(response.headers.get('Access-Control-Allow-Origin')).to.include(origin) expect(response.headers.get('Access-Control-Allow-Methods')).to.include('POST') expect(response.headers.get('Access-Control-Allow-Headers')).to.include('Authorization') }) }) }) })
经验总结
这次问题排查让我们学到了很多:
- 架构 设计 更要特别注意配置迁移的完整性
- 跨域配置要考虑全面,包括预检请求和各种场景
- 要建立完善的测试机制,及早发现问题
- 前端要有合适的错误处理机制
就像搬家时要仔细检查有没有遗漏重要物品一样,系统架构调整时也要特别注意配置的完整性。而且要像检查清单一样,把所有可能的场景都测试一遍。
写在最后
跨域问题虽然常见,但解决起来并不简单,特别是在复杂的微服务架构中。关键是要理解背后的原理,建立完善的测试机制,这样才能及时发现和解决问题。
有什么问题欢迎在评论区讨论,让我们一起提高技术水平!
> 如果觉得有帮助,别忘了点赞关注,我会继续分享更多实战经验~

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
【GreatSQL优化器-06】条件过滤导致选择非最佳
【GreatSQL优化器-06】条件过滤导致选择非最佳 一、condition_fanout_filter导致计划非最佳 GreatSQL 的优化器对于 join 的表需要根据行数和 cost 来确定最后哪张表先执行哪张表后执行,这里面就涉及到预估满足条件的表数据,condition_fanout_filter会根据一系列方法计算出一个数据过滤百分比,这个比百分比就是 filtered 系数,这个值区间在[0,1],值越小代表过滤效果越好。用这个系数乘以总的行数就可以得出最后需要扫描的表行数的数量,可以大幅节省开销和执行时间。 这个功能是由OPTIMIZER_SWITCH_COND_FANOUT_FILTER这个OPTIMIZER_SWITCH来控制的,默认是打开的。因此一般情况下不需要特意去关闭,但是如果遇到执行特别慢的一些情况可以考虑关闭。 下面用一个例子来说明condition_fanout_filter有可能导致选择错误的情况: # 创建2张表,都只在第二列创建索引,其中t3的最后一列也创建一个索引。 CREATE TABLE t3 (ccc1 INT, ccc2 int...
- 下一篇
百万现金奖励,冲刺鸿蒙生态建设“最后一公里”
过去十年,移动互联网以排山倒海之势彻底重塑了所有人的生活、工作模式。在此期间,浩如烟海的应用涌现,而在它们背后的开发者们也随之走进大众视野。技术的创新突破、商业的蓬勃发展、人才的汇聚交融以及梦想的熠熠光辉,所有要素相互交织、彼此促进,共同铸造了那个星光点点的移动互联网时代。 然而,时过境迁,在当下这个近乎全民皆网民的时代,移动互联网流量红利逐渐式微已成为各界公认的事实。简言之,如今应用想要获得流量,其难度可谓与日俱增。 1、终端开发进入稳定期:人们使用的 App 越来越固定,新应用的下载量很难有爆发式增长; 2、竞价排名白热化:为了在海量应用中脱颖而出,开发者需要在广告投放、社媒推广等渠道投入更多资金来吸引用户下载,获客成本不断上升; 3、盈利空间受阻:无论是付费应用还是靠广告盈利的应用,购买转化率与广告点击率均在下降 ...... 特别是相较于那些占据头部地位的应用而言,中长尾应用明显处于资源匮乏的劣势地位。在安卓、iOS 等主流移动生态体系中,即便再优秀的创意灵感,也常常被淹没于应用洪流之中。 后移动互联网时代,开发者的下一个“蓝海”在哪儿?是继续深耕传统,还是跳出安卓、iOS 体...
相关文章
文章评论
共有0条评论来说两句吧...