简历回答逐字稿:HATEOAS 兜底状态与错误标准化处理
关联:简历追问:HATEOAS 兜底状态与错误标准化处理、HATEOAS 架构下前端如何处理兜底状态或无网环境。
30 秒开场
HATEOAS 做完以后,前端确实会更依赖后端契约,所以我重点做了错误标准化。否则 happy path 很漂亮,一到接口抖动、动作缺失、链式导航失败,页面就会变得不可控。
我的做法是先把错误分层:资源加载失败、资源不存在、动作不可执行、契约缺失、网络错误、鉴权失败、状态冲突。SDK 把这些错误标准化后,再给 UI 一套统一策略:哪些隐藏、哪些禁用、哪些可重试、哪些要提示刷新或重新拉取。
如果面试官问:_links 拿不到页面是不是就废了?
我会说不能简单让页面白屏,但也不能让前端凭空猜业务规则。
如果资源本身没加载出来,那页面应该进入资源不可用或重试状态。如果资源数据有缓存,但动作契约没拿到,我会把它标记成“基于旧快照”或“动作不可确认”,关键动作不应该无提示地开放。
对于弱网场景,可以使用最后一次资源快照维持页面骨架,让用户至少能查看信息。但涉及提交的动作,需要更谨慎:要么禁用并提示网络恢复后再操作,要么进入离线 mutation 队列,恢复网络后再回放,并处理 409 冲突。
如果面试官追:动作缺失和无权限怎么区分?
我会这样回答:
单纯看没有 approve,前端无法知道是无权限还是当前状态不可审批。所以如果业务需要给用户解释,就应该由后端提供更明确的 disabled reason 或者错误资源。
我会把 UI 策略分成两类:
- 非关键动作:没有 relation 就不展示,减少干扰。
- 关键动作:如果用户预期强,比如审批页里的审批按钮,最好展示禁用态,并由后端返回原因,比如“当前状态不可审批”或“你不是审批人”。
但无论怎么展示,前端都不应该重新写一份 status === pending && role === manager 来解释。
如果面试官追:链式 follow 中间失败怎么办?
我会说这里要避免一个页面只要某个子资源失败就整体崩掉。
比如从 root follow 到 user,再 follow 到 workspace,再 follow 到 diagram。如果 workspace 加载失败,我会保留上一级 user 状态,并让 workspace 区域显示局部错误和重试。SDK 层会把错误带上 relation path,比如:
root -> default-user -> workspace这样 UI 和日志都知道是哪个 relation 断了,而不是只看到一个 fetch error。
如果失败是 401/403,就引导登录或权限提示;如果是 404,说明资源不存在或被删除;如果是 timeout,可以重试;如果是 invalid contract,就应该上报,因为这更像前后端契约事故。
如果面试官追:离线点击动作后,恢复网络状态变了怎么办?
我会这样说:
离线队列只能提升体验,不能绕过服务端状态机。用户离线点击 approve,本地可以生成一个 command,包含 action relation、resource id、payload、幂等 id、创建时间。UI 可以显示“待同步”,但不能说服务端已经最终成功。
网络恢复后 SDK 回放 command。如果服务端返回 409,比如这张单已经被别人撤回,我会停止乐观状态,拉取最新资源,并提示用户“操作未生效,因为资源状态已变化”。用户可以基于最新状态重新决策。
这里我会强调:离线不是前端自己变成权威状态机,最终合法性还是服务端契约和状态机决定。
如果面试官追:错误标准化有哪些类型?
我会列一个比较实用的枚举:
type ResourceError =
| 'NetworkError'
| 'Unauthorized'
| 'Forbidden'
| 'NotFound'
| 'ActionMissing'
| 'InvalidContract'
| 'PayloadValidationError'
| 'Conflict'
| 'ServerError'每个错误都带 resource uri、relation path、action name、http status、request id。这样 UI、日志和监控能对齐。
收尾句
这块我最核心的判断是:契约驱动 UI 不是只处理成功状态,而是要把失败状态也契约化。否则前端虽然少写了业务规则,却会在异常场景里变成不可控。