src/lib/api/client.ts:109 的兜底链只认两种形状的响应体,其它形状一律退化成状态码,把服务端给的信息全丢了(main @ 99029d7)。
const error = body?.status?.message ?? body?.message ?? `${httpStatus} ${statusText}`;
而 parseBody(:53-61)碰到非 JSON 时会把文本原样返回:
async function parseBody(resp: Response): Promise<any> {
const text = await resp.text();
if (!text) return null;
try {
return JSON.parse(text);
} catch {
return text;
}
}
于是下面这些响应体在界面上都是同一句话。我用仓库里真实的 client.ts 各跑了一遍(桩掉 fetch 让它返回 400 + 对应响应体,看最终抛给提示栏的是什么):
| 服务端实际返回的响应体 |
界面上显示 |
{"status":{"code":95001,"message":"数据校验失败:xxx"},…} |
"数据校验失败:xxx" |
{"status":{"code":95001,"message":""},…} |
""(空提示) |
{"timestamp":…,"status":400,"error":"Bad Request","path":"…"}(Spring 默认错误体) |
"400 Bad Request" |
<!doctype html><html>…400 Bad Request…</html> |
"400 Bad Request" |
Bad Request: failed to read request body(纯文本) |
"400 Bad Request" |
| 空响应体 |
"400 Bad Request" |
只有第一种(项目自己的信封)能把服务端的话带出来,后四种在界面上完全一样。
这不是假设出来的场景。今天有人在导入页上传音击的档案 JSON,只看到 400、拿不到任何原因,最后只能挂一个 window.fetch 钩子把原始响应体捞出来看。这条兜底链恰好把唯一能定位问题的信息丢掉了。
顺带一个对照,说明信封本身是好的:同一个后端对未鉴权请求会返回 {"status":{"code":94011,"message":"Unauthorized"},"time":"…","data":"Insufficient authentication"},那种情况界面上能正常显示。问题只出在"信封之外的形状"。
(那个导入 400 本身是什么引起的,我还没有结论——后端不开源,拿不到它的导入 DTO。这不影响本 issue:无论后端说了什么,前端现在都显示不出来。)
修法:
- const error = body?.status?.message ?? body?.message ?? `${httpStatus} ${statusText}`;
+ // 非信封形状的响应体也得能透出点什么,否则界面上只剩一个状态码
+ const raw = typeof body === 'string'
+ ? body.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 200)
+ : '';
+ const error = body?.status?.message || body?.message || raw || `${httpStatus} ${statusText}`;
我把这几行又按上面那张表格逐个跑了一遍:
| 响应体形状 |
现在 |
改后 |
| 自家信封(带 message) |
数据校验失败:xxx |
不变 |
| 自家信封,message 为空 |
""(空提示) |
400 Bad Request |
| Spring 默认错误体 |
400 Bad Request |
不变 |
| HTML 错误页 |
400 Bad Request |
不变(原文里就是这个词) |
| 纯文本 |
400 Bad Request |
Bad Request: failed to read request body |
| 空响应体 |
400 Bad Request |
不变 |
关键点是:现在能正常显示的那条路一个字都不动(信封里的 message 照旧原样透出,不会影响 parity 断言),只把"什么都显示不出来"的那几种情形补上。?? 换 || 也顺带修掉了空字符串弹空提示的问题。
(我第一版草稿想把状态码固定拼在前面,实测发现 HTML 错误页会变成 400 400 Bad Request,所以没采用。另外 body 是纯文本时 .status / .message 取不到值,可选链会安全跳过,raw 那条能兜住。)
src/lib/api/client.ts:109的兜底链只认两种形状的响应体,其它形状一律退化成状态码,把服务端给的信息全丢了(main@99029d7)。而
parseBody(:53-61)碰到非 JSON 时会把文本原样返回:于是下面这些响应体在界面上都是同一句话。我用仓库里真实的
client.ts各跑了一遍(桩掉fetch让它返回 400 + 对应响应体,看最终抛给提示栏的是什么):{"status":{"code":95001,"message":"数据校验失败:xxx"},…}"数据校验失败:xxx"{"status":{"code":95001,"message":""},…}""(空提示){"timestamp":…,"status":400,"error":"Bad Request","path":"…"}(Spring 默认错误体)"400 Bad Request"<!doctype html><html>…400 Bad Request…</html>"400 Bad Request"Bad Request: failed to read request body(纯文本)"400 Bad Request""400 Bad Request"只有第一种(项目自己的信封)能把服务端的话带出来,后四种在界面上完全一样。
这不是假设出来的场景。今天有人在导入页上传音击的档案 JSON,只看到 400、拿不到任何原因,最后只能挂一个
window.fetch钩子把原始响应体捞出来看。这条兜底链恰好把唯一能定位问题的信息丢掉了。顺带一个对照,说明信封本身是好的:同一个后端对未鉴权请求会返回
{"status":{"code":94011,"message":"Unauthorized"},"time":"…","data":"Insufficient authentication"},那种情况界面上能正常显示。问题只出在"信封之外的形状"。(那个导入 400 本身是什么引起的,我还没有结论——后端不开源,拿不到它的导入 DTO。这不影响本 issue:无论后端说了什么,前端现在都显示不出来。)
修法:
我把这几行又按上面那张表格逐个跑了一遍:
数据校验失败:xxx""(空提示)400 Bad Request400 Bad Request400 Bad Request400 Bad RequestBad Request: failed to read request body400 Bad Request关键点是:现在能正常显示的那条路一个字都不动(信封里的
message照旧原样透出,不会影响 parity 断言),只把"什么都显示不出来"的那几种情形补上。??换||也顺带修掉了空字符串弹空提示的问题。(我第一版草稿想把状态码固定拼在前面,实测发现 HTML 错误页会变成
400 400 Bad Request,所以没采用。另外body是纯文本时.status/.message取不到值,可选链会安全跳过,raw那条能兜住。)