From 459cabb1bc5b35bd4dbd55175785674dea8a9705 Mon Sep 17 00:00:00 2001 From: wanghanlin <1533525126@qq.com> Date: Thu, 3 Sep 2026 11:52:42 +0800 Subject: [PATCH] =?UTF-8?q?fix(mcp-server):=20=E4=BF=AE=E5=A4=8D=E5=90=AF?= =?UTF-8?q?=E7=94=A8=E5=BC=80=E5=85=B3=E8=A7=A6=E5=8F=91=20toggle/refresh?= =?UTF-8?q?=20=E8=AF=B7=E6=B1=82=E6=AD=BB=E5=BE=AA=E7=8E=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 前端 toggleMcpServer 补发 { active } 请求体,与后端 @RequestBody 契约一致(此前 active 拼在 query、无 body,每次点开关必 400) - 后端 toggleActive 兼容 query 兜底,避免旧调用方仍 400 - axios 拦截器加重试上限(_retry),刷新改用裸 axios 发起,切断 401→refresh→重试 自激递归 - MCP 开关失败回滚 _active 状态并加 _toggling 防连点 --- frontend/src/api/mcp-server.ts | 2 +- frontend/src/api/request.ts | 16 +++++++++++++--- frontend/src/views/McpServerManager.vue | 9 ++++++--- .../controller/McpServerConfigController.java | 13 +++++++++---- 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/frontend/src/api/mcp-server.ts b/frontend/src/api/mcp-server.ts index 6ca2d37..1f27ab5 100644 --- a/frontend/src/api/mcp-server.ts +++ b/frontend/src/api/mcp-server.ts @@ -11,7 +11,7 @@ export function getMcpServerDetail(id: string): Promise { return re export function createMcpServer(data: any): Promise { return request.post('/mcp-server', data).then(r => r.data) } export function updateMcpServer(id: string, data: any): Promise { return request.put(`/mcp-server/${id}`, data).then(r => r.data) } export function deleteMcpServer(id: string): Promise { return request.delete(`/mcp-server/${id}`).then(r => r.data) } -export function toggleMcpServer(id: string, active: boolean): Promise { return request.put(`/mcp-server/${id}/toggle?active=${active}`).then(r => r.data) } +export function toggleMcpServer(id: string, active: boolean): Promise { return request.put(`/mcp-server/${id}/toggle`, { active }).then(r => r.data) } export function testMcpServer(id: string): Promise { return request.post(`/mcp-server/${id}/test`, {}).then(r => r.data) } export function refreshMcpServers(): Promise { return request.post('/mcp-server/refresh', {}).then(r => r.data) } export function listAvailableMcpTools(): Promise { return request.get('/mcp-server/tools').then(r => r.data) } diff --git a/frontend/src/api/request.ts b/frontend/src/api/request.ts index 49d938e..f878ef9 100644 --- a/frontend/src/api/request.ts +++ b/frontend/src/api/request.ts @@ -42,6 +42,15 @@ request.interceptors.response.use( const url: string = config?.url || '' if (response?.status === 401 && !url.includes('/auth/login') && !url.includes('/auth/refresh')) { + // 重试上限:已经过「401 → refresh → 重试」一次的请求若仍 401,不再二次刷新, + // 直接失败,斩断 401→refresh→401 的自激死循环(配合 _retry 标记,见下方挂起分支) + if ((config as any)._retry) { + return Promise.reject(new Error(response?.data?.message || error.message || '登录已过期,请重新登录')) + } + // 标记本次请求已进入刷新流程(含挂起等待者),确保每个请求至多重试一次 + const retriedConfig = config as any + retriedConfig._retry = true + // 已有刷新在进行中,挂起当前请求,等待新 token 后重试 if (isRefreshing) { return new Promise((resolve, reject) => { @@ -55,17 +64,18 @@ request.interceptors.response.use( }) } - // 发起刷新(request 的 url 为 /auth/refresh,其 401 会被上方判断跳过,不会递归) + // 发起刷新(用「裸 axios」而不走当前实例,避免 refresh 自身再次进入本拦截器造成递归; + // 其 401 也会被上方 url 判断跳过,不会二次刷新) isRefreshing = true try { - const res = await request.post('/auth/refresh') + const res = await axios.post('/auth/refresh', null, { withCredentials: true }) const newToken = res.data?.data?.accessToken if (newToken) { setToken(newToken) // 放行所有挂起请求(请求拦截器会自动附加新 token) pendingQueue.forEach((cb) => cb(newToken)) pendingQueue = [] - // 重试当前请求 + // 重试当前请求(config._retry 已为 true,若仍 401 会直接失败而不再刷新) return request(config) } throw new Error('刷新令牌失败') diff --git a/frontend/src/views/McpServerManager.vue b/frontend/src/views/McpServerManager.vue index 06f63e2..de6995d 100644 --- a/frontend/src/views/McpServerManager.vue +++ b/frontend/src/views/McpServerManager.vue @@ -13,7 +13,7 @@ :pagination="{ current: currentPage, total: total, pageSize: pageSize, showJumper: true }" @page-change="onPageChange" @sort-change="onSortChange"> - +