# 设计:全站 API 失败统一文案与 Toast 提示 **日期**:2026-04-18 **状态**:已定稿(与产品对话一致) ## 1. 背景与目标 当前 `app/utils/http/factory.ts` 中 `request` 为裸 `$fetch` 实例;`unwrapApiBody` 在 `code !== 0` 时抛出带 `message` 的 `Error`。HTTP 层失败表现为 ofetch 的 `FetchError`(常见字段含 `statusMessage`、`data`)。各页面仅在自行 `catch` 时才会 `useToast()`,导致部分操作失败时 **无可见反馈**,且多处重复手写 `extractError`,同一类失败文案不一致。 **目标**: - 用户触发的 API 调用失败时,**默认**有一条 **可读中文 toast**(Nuxt UI `useToast`)。 - 文案优先来自服务端已有 **`statusMessage` / 业务 `message`**,避免空白或仅控制台错误。 - **单一实现** 解析 `unknown` 错误为展示字符串,收敛重复代码。 ## 2. 非目标 - 在 **SSR** 阶段弹出 toast(仅客户端展示)。 - 替代表单字段级校验(组件内仍可保留短提示)。 - 本次不强制改造 `_useHttpFetch` / 声明式 `useFetch` 用法(可作为后续增量)。 - 不改变服务端错误契约(仍用 `createError` + `ApiResponse` 等现有形态)。 ## 3. 方案结论(已选) 采用 **薄封装 + 统一解析**(否决全局 `$fetch` 钩子自动 toast:难以区分静默场景、与 `useAuthSession` 等冲突)。 | 构件 | 职责 | |------|------| | `getApiErrorMessage(error: unknown): string` | 单一模块,统一从 `Error` / `FetchError` 等解析用户可见文案。 | | `useClientApi()`(命名以实现为准) | 返回与 `request` 兼容的调用方法;**默认**在客户端失败时 `toast.add`(error)后 **原样 rethrow**,保证调用方 `finally`、状态回滚仍可用。 | | 选项 `notify: false` | 关闭本次请求的 toast(会话刷新、401 静默路径、登录页内联错误、自定义多段流程等)。 | ## 4. 文案解析规则 按顺序取值,命中即停: 1. 若对象为 FetchError 风格且 **`data` 为对象且存在非空 `message` 字符串** → 使用该 `message`。 2. 若非空 **`statusMessage` 字符串** → 使用。 3. 若为 `Error` 且 **`message` 非空**(含 `unwrapApiBody` 抛出的业务 `message`)→ 使用。 4. 若存在 **`statusCode`**:**401** → 「登录已失效,请重新登录」;**403** → 「没有权限执行此操作」;**404** → 「请求的资源不存在」;**5xx** → 「服务暂时不可用,请稍后重试」。 5. 无状态码或网络类失败 → 「网络异常,请检查连接后重试」或等价短句。 6. 兜底 → 「请求失败,请稍后重试」。 (具体措辞可在实现时微调,但优先级与分支须一致,避免再次出现多种解析逻辑。) ## 5. 401 与认证路径(已确认策略) - **`useAuthSession.refresh` / 通过 `useRequestFetch` 拉 `/api/auth/me` 的路径**:保持现有行为 — **不**因 401 弹业务 toast(`notify: false` 或继续使用裸 `request` / 现有 `catch` 逻辑);401 时清会话等逻辑不变。 - **其余用户触发的客户端 API**:默认 **`notify: true`**;若响应为 401,用户看到 **一条**与第 4 节一致的短文案(与「静默清会话」可并存:先 toast,路由或后续导航由现有布局/中间件处理)。 - **登录 / 注册页**:失败提示以 **表单内联** 为主;对应请求使用 **`notify: false`**,避免与内联文案重复。 ## 6. 迁移与重复 toast 防护 - 将 `app/pages/**`、`app/components/**` 中 **面向用户的** `request(...)` 逐步改为封装方法(默认 `notify: true`)。 - 若调用方已有 `catch` 且 **`toast.add`**,应 **删除** 其中重复的错误 toast,仅保留特殊成功提示或额外上下文(否则会双弹)。 - 保留 `unwrapApiBody` 与 `ApiResponse` 类型;封装内部仍调用现有 `request`。 ## 7. 测试与验收 - **手工**:至少覆盖 — 登录失败(内联、无重复 toast)、管理端创建用户失败、评论失败、上传失败、任意 403/404 接口。 - **自动化**:若有 e2e,可增一条「失败时出现全局通知」;否则以回归清单为准。 ## 8. 后续 定稿并实现后,由 **`writing-plans`** 产出 `docs/superpowers/plans/2026-04-18-api-error-toast-implementation-plan.md`(或同日合并命名),再进入编码。