Spring Boot项目fetch请求失败求助(已排除CORS问题)
问题排查:订阅变更请求触发TypeError: Failed to fetch
问题背景
- Spring Boot教学项目部署在8080端口,前端包含个人数据修改和订阅变更模块
- 个人数据修改请求8080控制器完全正常;但订阅变更时,前端请求8080的订阅控制器,该控制器调用同端口支付控制器,支付控制器向8081端口的模拟银行服务发请求——订阅实际成功,但前端返回
TypeError: Failed to fetch - 已尝试过:在8080、8081端口单独/同时配置CORS,给控制器加CORS注解,修改WebSecurityConfig等操作,甚至还原代码仅保留WebSecurityConfig的CORS配置,问题依旧
- 经测试排除CORS问题:直接调用
updateSubscription函数时请求正常,但通过displayAllSubscription生成的按钮调用该函数时,代码在fetch请求处停止执行,无响应返回
相关代码
displayAllSubscription函数
function displayAllSubscription() { const findAllSubmenu = document.getElementById('findAllSubmenu'); if (!findAllSubmenu) { console.error('Element with id "findAllSubmenu" not found.'); return; } findAllSubmenu.innerHTML = '<ul>'; subscriptions.forEach(subscription => { const subscriptionName = subscription.subscriptionName || 'Unknown'; const subscriptionPrice = subscription.subscriptionPrice !== null && subscription.subscriptionPrice !== undefined ? subscription.subscriptionPrice : 'N/A'; const subscriptionDurationTime = subscription.subscriptionDurationTime !== null && subscription.subscriptionDurationTime !== undefined ? subscription.subscriptionDurationTime : 'N/A'; findAllSubmenu.innerHTML += ` <li class="submenu-all-subscriptions-item"> <p class="submenu-all-subscriptions-name">Name: ${subscriptionName}</p> <p class="submenu-all-subscriptions-name">Price: ${subscriptionPrice}</p> <p class="submenu-all-subscriptions-name">Duration time: ${subscriptionDurationTime}</p> <button class="submenu-all-subscriptions-button" onclick="updateSubscription('${subscriptionName}')">Subscribe</button> </li> `; }); findAllSubmenu.innerHTML += '</ul>'; findAllSubmenu.style.display = 'block'; }
updateSubscription函数
function updateSubscription(subscriptionName) { const jwtToken = getCookie('JWT_TOKEN'); if (!jwtToken) { console.error('JWT token not found'); return; } if (!subscriptionName) { console.error('Subscription name is required'); return; } const requestData = { newSubscription: { subscriptionName: subscriptionName } }; fetch('/personal_office/update_subscription', { method: 'PUT', headers: { 'Authorization': `Bearer ${jwtToken}`, 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify(requestData) }) .then(response => { if (!response.ok) { return response.text().then(text => { throw new Error(text); }); } return response.text(); }) .then(responseMessage => { displayMessage(responseMessage, 'success'); showSubscriptionOnScreen(); showSubscriptionEndTimeOnScreen(); }) .catch(error => { console.error('Failed to update subscription: ', error); displayMessage(error.message, 'error'); }); }
问题根源
核心问题出在按钮onclick属性的字符串拼接逻辑:
如果subscriptionName包含空格、单引号(比如'VIP Subscription')这类特殊字符,拼接后会导致onclick里的JS代码语法错误。例如订阅名称是'Joe's Plan',拼接后onclick会变成onclick="updateSubscription('Joe's Plan')",直接触发语法错误,函数根本没执行,自然不会发起fetch请求,浏览器最终抛出TypeError: Failed to fetch(实际是代码执行中断)。
解决方案
方案1:改用事件绑定(推荐)
避免字符串拼接生成事件,直接创建DOM元素并绑定click事件,彻底规避字符串转义问题:
function displayAllSubscription() { const findAllSubmenu = document.getElementById('findAllSubmenu'); if (!findAllSubmenu) { console.error('Element with id "findAllSubmenu" not found.'); return; } // 清空容器并创建ul findAllSubmenu.innerHTML = ''; const ul = document.createElement('ul'); subscriptions.forEach(subscription => { const subscriptionName = subscription.subscriptionName || 'Unknown'; const subscriptionPrice = subscription.subscriptionPrice !== null && subscription.subscriptionPrice !== undefined ? subscription.subscriptionPrice : 'N/A'; const subscriptionDurationTime = subscription.subscriptionDurationTime !== null && subscription.subscriptionDurationTime !== undefined ? subscription.subscriptionDurationTime : 'N/A'; // 创建li元素 const li = document.createElement('li'); li.className = 'submenu-all-subscriptions-item'; li.innerHTML = ` <p class="submenu-all-subscriptions-name">Name: ${subscriptionName}</p> <p class="submenu-all-subscriptions-name">Price: ${subscriptionPrice}</p> <p class="submenu-all-subscriptions-name">Duration time: ${subscriptionDurationTime}</p> `; // 创建按钮并绑定点击事件 const subscribeBtn = document.createElement('button'); subscribeBtn.className = 'submenu-all-subscriptions-button'; subscribeBtn.textContent = 'Subscribe'; // 直接绑定函数,传递正确的subscriptionName subscribeBtn.addEventListener('click', () => updateSubscription(subscriptionName)); li.appendChild(subscribeBtn); ul.appendChild(li); }); findAllSubmenu.appendChild(ul); findAllSubmenu.style.display = 'block'; }
方案2:对订阅名称进行转义
如果坚持用字符串拼接,需要对subscriptionName中的单引号进行转义,避免语法冲突:
// 在拼接前转义单引号 const escapedSubscriptionName = subscriptionName.replace(/'/g, "\\'"); // 然后用转义后的名称拼接按钮 findAllSubmenu.innerHTML += ` <li class="submenu-all-subscriptions-item"> <p class="submenu-all-subscriptions-name">Name: ${subscriptionName}</p> <p class="submenu-all-subscriptions-name">Price: ${subscriptionPrice}</p> <p class="submenu-all-subscriptions-name">Duration time: ${subscriptionDurationTime}</p> <button class="submenu-all-subscriptions-button" onclick="updateSubscription('${escapedSubscriptionName}')">Subscribe</button> </li> `;
内容的提问来源于stack exchange,提问作者PavelPavlik
相关产品推荐
相关产品推荐

