From 897220e19a72c9f84cbdd4784f55ee5ce69576d4 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 2 May 2026 20:36:00 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20Vben5.0=20download?= =?UTF-8?q?=20=E6=8E=A5=E5=8F=A3=20token=20=E8=BF=87=E6=9C=9F=E4=B8=8D?= =?UTF-8?q?=E8=A7=A6=E5=8F=91=E5=88=B7=E6=96=B0=EF=BC=8C=E5=AF=BC=E5=87=BA?= =?UTF-8?q?/=E4=B8=8B=E8=BD=BD=E6=96=87=E4=BB=B6=E5=8F=98=E6=88=90?= =?UTF-8?q?=E3=80=8C=E8=B4=A6=E5=8F=B7=E6=9C=AA=E7=99=BB=E5=BD=95=E3=80=8D?= =?UTF-8?q?JSON=EF=BC=9Bweb-antd=20/=20web-ele=20/=20web-naive=20/=20web-t?= =?UTF-8?q?design=20=E5=8A=A0=20Blob=20=E4=B8=9A=E5=8A=A1=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E5=97=85=E6=8E=A2=E6=8B=A6=E6=88=AA=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web-antd/src/api/request.ts | 43 +++++++++++++++++++++++++++++ apps/web-ele/src/api/request.ts | 43 +++++++++++++++++++++++++++++ apps/web-naive/src/api/request.ts | 43 +++++++++++++++++++++++++++++ apps/web-tdesign/src/api/request.ts | 43 +++++++++++++++++++++++++++++ 4 files changed, 172 insertions(+) diff --git a/apps/web-antd/src/api/request.ts b/apps/web-antd/src/api/request.ts index 15539ab93..7389c31ad 100644 --- a/apps/web-antd/src/api/request.ts +++ b/apps/web-antd/src/api/request.ts @@ -128,6 +128,49 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) { }, }); + // add by 芋艿:对应 https://t.zsxq.com/SHqWw 反馈 + // 处理 Blob 响应中的业务错误(如 401):后端把「账号未登录」包成 HTTP 200 + body {code: 401, msg: ...}, + // download 强制 responseType: 'blob' 后被 axios 包成 application/json 的 Blob,defaultResponseInterceptor 走 + // responseReturn === 'body' 分支直接返回,绕过了 authenticateResponseInterceptor 的 401 token 刷新; + // 这里把这种 Blob 解析回 JSON,再以 axios 风格抛出,让后续拦截器接管 + client.addResponseInterceptor({ + fulfilled: async (response) => { + const blob = response.data; + if (!(blob instanceof Blob)) { + return response; + } + // Blob.type 在部分环境可能为空或大小写不一,叠加 response header 一起判断更稳 + const blobType = (blob.type || '').toLowerCase(); + const headerType = String( + response.headers?.['content-type'] ?? + response.headers?.['Content-Type'] ?? + '', + ).toLowerCase(); + if ( + !blobType.includes('application/json') && + !headerType.includes('application/json') + ) { + return response; + } + let parsed: any; + try { + parsed = JSON.parse(await blob.text()); + } catch { + return response; + } + if (parsed && parsed.code !== undefined && parsed.code !== 0) { + response.data = parsed; + throw Object.assign(new Error(parsed.msg ?? 'Request failed'), { + config: response.config, + response, + data: parsed, + isAxiosError: true, + }); + } + return response; + }, + }); + // 处理返回的响应数据格式 client.addResponseInterceptor( defaultResponseInterceptor({ diff --git a/apps/web-ele/src/api/request.ts b/apps/web-ele/src/api/request.ts index 0675f912c..f221d4efa 100644 --- a/apps/web-ele/src/api/request.ts +++ b/apps/web-ele/src/api/request.ts @@ -128,6 +128,49 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) { }, }); + // add by 芋艿:对应 https://t.zsxq.com/SHqWw 反馈 + // 处理 Blob 响应中的业务错误(如 401):后端把「账号未登录」包成 HTTP 200 + body {code: 401, msg: ...}, + // download 强制 responseType: 'blob' 后被 axios 包成 application/json 的 Blob,defaultResponseInterceptor 走 + // responseReturn === 'body' 分支直接返回,绕过了 authenticateResponseInterceptor 的 401 token 刷新; + // 这里把这种 Blob 解析回 JSON,再以 axios 风格抛出,让后续拦截器接管 + client.addResponseInterceptor({ + fulfilled: async (response) => { + const blob = response.data; + if (!(blob instanceof Blob)) { + return response; + } + // Blob.type 在部分环境可能为空或大小写不一,叠加 response header 一起判断更稳 + const blobType = (blob.type || '').toLowerCase(); + const headerType = String( + response.headers?.['content-type'] ?? + response.headers?.['Content-Type'] ?? + '', + ).toLowerCase(); + if ( + !blobType.includes('application/json') && + !headerType.includes('application/json') + ) { + return response; + } + let parsed: any; + try { + parsed = JSON.parse(await blob.text()); + } catch { + return response; + } + if (parsed && parsed.code !== undefined && parsed.code !== 0) { + response.data = parsed; + throw Object.assign(new Error(parsed.msg ?? 'Request failed'), { + config: response.config, + response, + data: parsed, + isAxiosError: true, + }); + } + return response; + }, + }); + // 处理返回的响应数据格式 client.addResponseInterceptor( defaultResponseInterceptor({ diff --git a/apps/web-naive/src/api/request.ts b/apps/web-naive/src/api/request.ts index 23888b64c..39e4613df 100644 --- a/apps/web-naive/src/api/request.ts +++ b/apps/web-naive/src/api/request.ts @@ -127,6 +127,49 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) { }, }); + // add by 芋艿:对应 https://t.zsxq.com/SHqWw 反馈 + // 处理 Blob 响应中的业务错误(如 401):后端把「账号未登录」包成 HTTP 200 + body {code: 401, msg: ...}, + // download 强制 responseType: 'blob' 后被 axios 包成 application/json 的 Blob,defaultResponseInterceptor 走 + // responseReturn === 'body' 分支直接返回,绕过了 authenticateResponseInterceptor 的 401 token 刷新; + // 这里把这种 Blob 解析回 JSON,再以 axios 风格抛出,让后续拦截器接管 + client.addResponseInterceptor({ + fulfilled: async (response) => { + const blob = response.data; + if (!(blob instanceof Blob)) { + return response; + } + // Blob.type 在部分环境可能为空或大小写不一,叠加 response header 一起判断更稳 + const blobType = (blob.type || '').toLowerCase(); + const headerType = String( + response.headers?.['content-type'] ?? + response.headers?.['Content-Type'] ?? + '', + ).toLowerCase(); + if ( + !blobType.includes('application/json') && + !headerType.includes('application/json') + ) { + return response; + } + let parsed: any; + try { + parsed = JSON.parse(await blob.text()); + } catch { + return response; + } + if (parsed && parsed.code !== undefined && parsed.code !== 0) { + response.data = parsed; + throw Object.assign(new Error(parsed.msg ?? 'Request failed'), { + config: response.config, + response, + data: parsed, + isAxiosError: true, + }); + } + return response; + }, + }); + // 处理返回的响应数据格式 client.addResponseInterceptor( defaultResponseInterceptor({ diff --git a/apps/web-tdesign/src/api/request.ts b/apps/web-tdesign/src/api/request.ts index 30cd83983..1d3038bb4 100644 --- a/apps/web-tdesign/src/api/request.ts +++ b/apps/web-tdesign/src/api/request.ts @@ -127,6 +127,49 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) { }, }); + // add by 芋艿:对应 https://t.zsxq.com/SHqWw 反馈 + // 处理 Blob 响应中的业务错误(如 401):后端把「账号未登录」包成 HTTP 200 + body {code: 401, msg: ...}, + // download 强制 responseType: 'blob' 后被 axios 包成 application/json 的 Blob,defaultResponseInterceptor 走 + // responseReturn === 'body' 分支直接返回,绕过了 authenticateResponseInterceptor 的 401 token 刷新; + // 这里把这种 Blob 解析回 JSON,再以 axios 风格抛出,让后续拦截器接管 + client.addResponseInterceptor({ + fulfilled: async (response) => { + const blob = response.data; + if (!(blob instanceof Blob)) { + return response; + } + // Blob.type 在部分环境可能为空或大小写不一,叠加 response header 一起判断更稳 + const blobType = (blob.type || '').toLowerCase(); + const headerType = String( + response.headers?.['content-type'] ?? + response.headers?.['Content-Type'] ?? + '', + ).toLowerCase(); + if ( + !blobType.includes('application/json') && + !headerType.includes('application/json') + ) { + return response; + } + let parsed: any; + try { + parsed = JSON.parse(await blob.text()); + } catch { + return response; + } + if (parsed && parsed.code !== undefined && parsed.code !== 0) { + response.data = parsed; + throw Object.assign(new Error(parsed.msg ?? 'Request failed'), { + config: response.config, + response, + data: parsed, + isAxiosError: true, + }); + } + return response; + }, + }); + // 处理返回的响应数据格式 client.addResponseInterceptor( defaultResponseInterceptor({