错误处理
API 错误统一返回 success: false、可展示的 error 和用于程序判断的 code。
错误结构
{
"success": false,
"error": "错误描述",
"code": "ERROR_CODE",
"message": "补充说明(可选)",
"rateLimit": {
"remaining": 147,
"limit": 150,
"reset": "2026-08-24T16:00:00.000Z"
}
}
错误码
- Name
INVALID_PARAMETER- Type
- 400
- Description
GET 的
word为空或超过 100 个字符;批量请求不是对象、words为空、超过 50 项,或数组内含无效词条。
- Name
NOT_FOUND- Type
- 404
- Description
没有完整记忆卡片,并且未启用或未命中本地词典兜底。
- Name
RATE_LIMIT_EXCEEDED- Type
- 429
- Description
当前 IP 的每日公开 API 配额已经用尽。
- Name
INSUFFICIENT_QUOTA- Type
- 429
- Description
批量请求需要的配额多于当前剩余配额。
- Name
INTERNAL_ERROR- Type
- 500
- Description
服务器内部错误。
- Name
NETWORK_ERROR- Type
- client
- Description
这不是服务端错误码,而是调用方可自行定义的网络失败状态。
推荐处理方式
async function queryWord(word, lang = 'en') {
const params = new URLSearchParams({
word,
lang,
includeLocalDictionaryFallback: 'true',
})
let response
try {
response = await fetch(
`https://api.keykey.im/api/public/memory-card?${params}`,
{ cache: 'no-store' },
)
} catch (error) {
return {
success: false,
code: 'NETWORK_ERROR',
error: '网络连接失败,请稍后重试',
}
}
const payload = await response.json().catch(() => ({
success: false,
code: 'INVALID_RESPONSE',
error: '服务器返回了无法解析的数据',
}))
if (response.ok && payload.success) {
return payload
}
switch (payload.code) {
case 'NOT_FOUND':
return { ...payload, error: '未找到匹配词条' }
case 'RATE_LIMIT_EXCEEDED':
case 'INSUFFICIENT_QUOTA':
return {
...payload,
error: `公开 API 配额不足,将在 ${payload.rateLimit?.reset ?? '稍后'} 重置`,
}
case 'INVALID_PARAMETER':
return { ...payload, error: '请输入有效词条' }
default:
return { ...payload, error: payload.error || '查词失败,请稍后重试' }
}
}
何时重试
NOT_FOUND和INVALID_PARAMETER:不要自动重试,修改输入或启用本地词典兜底。RATE_LIMIT_EXCEEDED和INSUFFICIENT_QUOTA:等待rateLimit.reset,不要立即重试。INTERNAL_ERROR、网络超时或连接失败:可使用最多 2-3 次、带抖动的指数退避。
async function fetchWithRetry(url, attempts = 3) {
for (let attempt = 0; attempt < attempts; attempt += 1) {
try {
const response = await fetch(url)
const payload = await response.json()
if (response.ok || response.status < 500) return payload
} catch (error) {
if (attempt === attempts - 1) throw error
}
const delay = 500 * 2 ** attempt + Math.random() * 250
await new Promise((resolve) => setTimeout(resolve, delay))
}
}
安全提示
将服务端的 error 当作普通文本展示,不要使用 dangerouslySetInnerHTML。记录问题时可保存 HTTP 状态、code 和请求语言,但不应记录用户的身份凭证或不必要的网络信息。