静态HTML页面使用原生JavaScript获取远程JSON元数据时遭遇跨域请求拦截的问题排查
这个问题我太熟悉了,咱们一步步拆解原因和解决方案:
为什么浏览器直接访问URL没问题,但Fetch就报错?
这完全是浏览器的**同源策略(Same Origin Policy)**在搞事情:
- 直接在地址栏输入URL属于浏览器的「普通导航请求」,不属于脚本发起的AJAX/Fetch请求范畴,浏览器不会强制执行CORS的严格校验,只要服务器返回200状态码和内容,就能正常显示。
- 但你的静态HTML页面发起的Fetch请求属于「跨域脚本请求」:如果你的页面域名(比如
localhost或者自己的静态站点域名)和目标URLhttps://example.source.host不是同一个域名(协议、域名、端口任意一个不同就算跨域),浏览器会自动触发CORS校验流程:浏览器会检查目标服务器返回的响应头中是否包含
Access-Control-Allow-Origin字段,这个字段需要明确允许当前页面的域名,或者设为*允许所有域名访问。
你的服务器返回的响应里完全没有这个头,所以浏览器直接拦截了响应内容,哪怕服务器实际返回了200和JSON数据,脚本也拿不到,就抛出了你看到的CORS错误。
你的代码还有个小坑(顺便提一下)
你代码里的if (data)逻辑永远不会执行,因为Fetch是异步操作:
let data; // Fetch是异步的,这里不会等待请求完成就往下走 fetch(...) .then(data => console.log('JSON response:', data)) // 这里的data是回调里的局部变量,和外面的let data不是同一个! if (data) { ... }
正确的做法是把数据处理逻辑放到then回调里,修改后大概是这样:
fetch(URL_SOURCE, { method: 'GET', headers: { // some headers } }) .then(response => { // 先检查响应状态是否正常 if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`); return response.json(); }) .then(data => { console.log('JSON response:', data); // 数据处理逻辑放到这里 try { const nowData = data?.response?.informationjson?.getNowData; console.log('Listening to:', nowData?.songtitle); } catch (error) { console.warn('Error reading source data! ', error.message); } }) .catch(error => console.error('Error data source:', error));
解决方案分两种情况
情况1:你能控制目标服务器(https://example.source.host)
最简单的办法是在服务器的响应头中添加CORS相关字段:
- 开发环境可以先加
Access-Control-Allow-Origin: *,允许所有域名访问; - 生产环境建议指定具体允许的域名,比如
Access-Control-Allow-Origin: https://your-static-page-domain.com,更安全; - 如果你的请求带了自定义请求头,还需要添加
Access-Control-Allow-Headers来允许对应的头,同时服务器需要处理OPTIONS预检请求(大部分后端框架都有现成的中间件可以处理,比如Node.js的cors包、Java的Spring Cors配置等)。
情况2:你不能控制目标服务器
这时候就得绕开浏览器的同源策略,常用的办法有两个:
搭建反向代理:
在你自己的服务器上搭建一个代理接口,让静态页面请求这个代理接口,代理接口再去请求目标URL,最后把响应返回给页面。因为服务器之间的请求不受浏览器同源策略限制。
举个简单的Node.js代理例子(用Express框架):const express = require('express'); const fetch = require('node-fetch'); const app = express(); // 代理接口 app.get('/proxy-meta', async (req, res) => { try { const targetResponse = await fetch('https://example.source.host', { headers: { // 这里放你原来的请求头 } }); const targetData = await targetResponse.json(); // 给响应添加允许跨域的头 res.setHeader('Access-Control-Allow-Origin', '*'); res.json(targetData); } catch (err) { res.status(500).json({ error: err.message }); } }); app.listen(3000, () => console.log('Proxy server running on http://localhost:3000'));然后把你页面里的
URL_SOURCE改成http://localhost:3000/proxy-meta即可。使用浏览器CORS扩展(仅用于开发测试):
比如Firefox的「CORS Everywhere」、Chrome的「Allow CORS: Access-Control-Allow-Origin」,安装后可以临时绕过浏览器的CORS校验,但这只适合你自己开发测试用,不能让生产环境的用户这么做。
内容的提问来源于stack exchange,提问作者haoz

