Fetch跨域请求问题:JSON解析后custom_css_final返回undefined
问题排查与解决方法
核心问题分析
通过GTM加载的脚本中,fetch获取到的响应文本看似完整,但JSON.parse(a).custom_css_final返回undefined,直接在控制台运行代码却正常。主要原因及排查方向如下:
1. 响应文本存在隐藏字符(BOM/空白)
服务器返回Content-Type: text/plain,手动序列化JSON后可能存在UTF-8 BOM或前后多余空白/换行符。虽然console.log(a)显示完整,但这些隐藏字符会导致JSON.parse解析后的对象结构异常,甚至静默失败。
2. 脚本变量名笔误
代码中创建了css2变量,但后续操作的是未定义的css变量:
var css2 = document.createElement('style'); css.id = 'newcss' // 应为css2.id css.setAttribute('type', 'text/css'); // 应为css2.setAttribute
这虽不是custom_css_final为undefined的直接原因,但会引发DOM操作异常,干扰问题判断。
3. Promise缓存与重复执行风险
getstuff是提前初始化的Promise,若GTM重复触发脚本加载,会导致Promise缓存旧响应或出现竞态条件。
解决步骤
步骤1:修复变量名笔误
将所有css变量替换为css2,确保DOM操作正确:
const dostuff = () => { getstuff.then((a) => { console.log(a); console.log(JSON.parse(a).custom_css_final); var css2 = document.createElement('style'); css2.id = 'newcss' css2.setAttribute('type', 'text/css'); document.getElementsByTagName('head')[0].appendChild(css2); setTimeout(function() { if (css2.styleSheet) { css2.styleSheet.cssText = JSON.parse(a).custom_css_final; } else { css2.appendChild( document.createTextNode(JSON.parse(a).custom_css_final) ); } }, 300) }) }
步骤2:净化响应文本并添加错误捕获
在JSON.parse前去除响应文本的空白和BOM,同时添加错误捕获定位问题:
const getstuff = fetch("https://example.net/users/abc") .then((response) => response.text()) .then((data) => { // 去除前后空白及UTF-8 BOM const cleanedData = data.trim().replace(/^\uFEFF/, ''); // 验证JSON格式 try { JSON.parse(cleanedData); return cleanedData; } catch (e) { console.error('JSON解析失败:', e, '原始数据:', data); throw e; } }) .catch(err => console.error('Fetch请求失败:', err));
步骤3:修改服务器响应头为application/json
将服务器端content-type改为application/json,让fetch直接解析为JSON对象,避免手动处理文本的问题:
var data = { "status": false, "cool":"cool", "custom_css_final":".something { color: #00FF00; }" } var json = JSON.stringify(data) const init_no_authresponse = { headers: { "content-type": "application/json", // 修改为application/json "Access-Control-Allow-Origin": "*", "Cache-Control": "no-store, max-age=0" }, status: 200 } return new Response(json, init_no_authresponse)
同步修改脚本中的fetch逻辑:
const getstuff = fetch("https://example.net/users/abc") .then((response) => response.json()) // 直接解析为JSON对象 .catch(err => console.error('Fetch或解析失败:', err)); const dostuff = () => { getstuff.then((a) => { console.log(a); console.log(a.custom_css_final); // 无需手动parse var css2 = document.createElement('style'); css2.id = 'newcss' css2.setAttribute('type', 'text/css'); document.getElementsByTagName('head')[0].appendChild(css2); setTimeout(function() { if (css2.styleSheet) { css2.styleSheet.cssText = a.custom_css_final; } else { css2.appendChild( document.createTextNode(a.custom_css_final) ); } }, 300) }) }
步骤4:确保脚本只执行一次
在脚本开头添加执行标记,避免GTM重复触发:
if (window.hasRunCustomCSSScript) return; window.hasRunCustomCSSScript = true; // 后续getstuff、dostuff代码放在此处
内容的提问来源于stack exchange,提问作者bmiskie
相关产品推荐
相关产品推荐

