Ky 2 错误钩子读 JSON 报 Already read
升级 Ky 2 后,beforeError 再读 response.json 可能遇到已消费的响应体。改用 error.data,并按 HTTPError、空数据和错误字段类型分别处理。
从 Ky 1 升级到 Ky 2,服务器仍返回原来的错误 JSON,但集中处理错误的钩子开始报 TypeError: Already read。如果代码在 beforeError 中再次调用 error.response.json(),应先改掉这一处读取方式。
Ky 2 会在调用错误钩子前读取 HTTP 错误响应,将结果放入 error.data。响应流已经被消费,再从同一 Response 读取 JSON,自然无法得到第二份正文。具体运行时也可能使用“body already used”一类措辞。
错误正文已经放在 data 里
这里的变化适用于 HTTPError。网络连接失败、请求取消和其他错误,并不保证存在 response。旧代码若直接访问 error.response,也可能掩盖原来的异常。
正式 Ky 2.2.0 中,beforeError 接收包含 error 等字段的状态对象。下面的示例只在响应数据确实带有字符串错误信息时替换 message,其余信息保留原值:
import ky, { isHTTPError } from 'ky';
const api = ky.create({
hooks: {
beforeError: [
({ error }) => {
if (isHTTPError(error)) {
const data = error.data;
if (data !== null && typeof data === 'object'
&& 'error_message' in data
&& typeof data.error_message === 'string') {
error.message = data.error_message;
}
}
return error;
},
],
},
});
error_message 是这个示例约定的服务端字段,需要替换成接口真实协议。不要把整个响应对象转换成字符串塞进 message;那既可能失去可读性,也可能把敏感业务数据写进错误日志。
旧式的 async error => ... 也要一并核对。不能只把 response.json() 改成 data,却把 Ky 2 传入的整个状态对象继续当成 Error。
data 不保证是某个对象
Ky 根据响应内容类型解析错误正文。JSON 可能得到对象,也可能是数组、数字或 null;其他内容类型可能得到文本。空响应、解析失败或无法读取时,data 可以是 undefined。TypeScript 中给 HTTPError 指定泛型,只影响静态类型,不会替服务器验证返回值。
如果原来还需要状态码或响应头,可以继续从 HTTPError 的 response 读取这些元数据。此时调用 response.clone().json() 也不能让已经消费的正文恢复:克隆必须发生在消费之前,不能当成这里的补救。
Ky 2.2.0 的错误数据读取有大小和时间边界。超大错误页或一直不结束的流,不能被当成必然可用的 JSON。保留没有业务 message 时的默认异常,不要为了取错误说明取消原来的超时或无限缓存响应体。
用同一组错误响应验收迁移
在应用原来的请求封装中检查 JSON 错误对象、非 JSON 错误页、空错误正文和网络失败。前者应读到约定信息,后几种仍应返回可识别的错误,不能再被二次读取异常覆盖。
若某些接口使用 throwHttpErrors: false,非成功状态不会走同样的 HTTPError 路径,应沿原有的状态判断处理。此次调整只处理错误数据的消费与钩子签名,不改变重试、认证或成功响应的解析方式。


