You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 20:37:16