某大厂前端负责人上周在内部技术会上摔了键盘——不是 metaphor,是真的摔了。导火索是一份接口文档:32个字段里17个是null,5个命名风格混用,还有3个字段类型和文档对不上。后端团队回复:"能用就行,别挑了。"
这不是技术债,这是协作癌。
REST API 从2000年 Fielding 的博士论文走到今天,成了"能跑就行"的遮羞布。我见过最荒唐的案例:某电商平台的库存接口,查询时返回字符串"0",扣减时要求传数字0,同步时又变成布尔值false。前端为了兼容,写了47行类型转换代码,注释写着「后端说下周改,2021年写的」。
坑一:把"资源"当成"数据库表"原样输出
REST 的核心是资源抽象,不是 SQL 结果集直出。我见过一个用户详情接口返回了127个字段,包含密码哈希、内部审计标记、甚至上游系统的原始错误日志。前端只需要昵称和头像,却要在内存里扛着整个数据仓库。
GraphQL 当年能火,不是因为技术多先进,是前端被这种"全量返回"逼疯了。但大多数团队没条件切 GraphQL,于是前端开始自己做字段过滤——在客户端写死白名单,或者更常见的,忍着。
正确的做法是分版本、分场景设计端点。/users/me 给 C 端,/users/admin/{id} 给运营后台,/users/internal/sync 给内部系统。三个端点,三套字段,而不是一个端点试图讨好所有人。
坑二:HTTP 状态码当摆设,200 里藏错误
这是行业通病。某支付接口,银行卡余额不足返回 HTTP 200,body 里嵌套 code: "INSUFFICIENT_BALANCE"。前端拦截器以为成功了,直接跳转成功页,用户看着"支付成功"的动画一脸懵。
HTTP 协议花了三十年定义状态码,不是让你当装饰品的。4xx 是客户端错,5xx 是服务端错,2xx 是成功。业务错误可以用 422(Unprocessable Entity)或 409(Conflict),别躲在 200 里玩捉迷藏。
更隐蔽的问题是缓存。CDN 和浏览器会缓存 200 响应,但不会缓存 4xx/5xx。你把错误包在 200 里,等于告诉全世界"这个结果可以存起来",下次用户看到的可能是一周前的错误信息。
坑三:分页设计得像随机数生成器
offset/limit 是最常见的分页,也是最容易出事的。数据在翻页过程中被插入或删除,用户会看到重复项或跳过项。更糟的是深分页:offset=100000 时,数据库要做全表扫描排序,查询时间从 10ms 变成 3s。
游标分页(cursor-based)能解决一致性,但实现复杂。我见过一个折中方案:列表页用 offset 保证简单,搜索/筛选结果用游标保证准确。关键是文档里要说清楚,而不是让前端猜"这个接口能不能支持深翻页"。
还有一个细节:总页数。大数据场景下,count(*) 是性能杀手。有些接口干脆不返回总数,前端就不知道怎么渲染分页器。折中方案是返回"是否有下一页"的布尔值,或者预估数量带误差范围。
坑四:API 版本管理靠"约定俗成"
/v1/users 改成 /v2/users,旧版本什么时候下线?我见过一个产品同时跑着 v1 到 v4,因为"不知道谁在调用"。最后靠日志分析+邮件轰炸,花了三个月才敢 deprecate v1。
版本号放在 URL 还是 Header,业界没统一标准。但比位置更重要的是生命周期:每个版本明确的 EOL(End of Life)日期,提前 6 个月通知,埋点监控调用量。这些流程比技术实现难十倍,却是协作的基础。
Breaking change 的定义也要明确。加字段通常安全,删字段、改类型、改枚举值都是 breaking。某次一个状态码从 "PENDING" 改成 "PENDING_PAYMENT",前端枚举匹配失败,整个订单流程瘫痪——这在语义上算"兼容改动"还是"破坏性更新"?
坑五:文档和代码是两部科幻小说
Swagger/OpenAPI 自动生成解决了"文档过时"问题,但带来了新问题:自动生成的文档往往暴露内部实现细节,字段描述是"created_at: 创建时间"这种废话。真正需要说明的——这个字段什么场景会为空、枚举值的业务含义、并发时的行为——全靠口头传承。
更隐蔽的是示例数据。文档里的示例是理想情况,生产环境可能是 edge case 的集合。我见过一个地址字段,示例是"北京市海淀区",实际出现过空字符串、null、"暂未填写"、JSON 对象、甚至 HTML 片段——因为上游系统对接了五个不同的渠道。
前端防御性编程的成本,往往比后端写文档高一个数量级。每个字段都要做类型检查、空值处理、长度截断、XSS 过滤,这些代码不会出现在需求文档里,但会出现在代码评审的吐槽里。
坑六:错误信息写给机器看,不是给人看
"Error code: 0x3F2A" 这种错误,前端没法展示给用户,只能写死映射表。映射表和后端代码不同步,用户看到的提示就莫名其妙。
好的错误响应应该包含三层:机器识别的 code(用于日志和监控)、前端可用的 message key(用于国际化映射)、可选的 detail(用于调试)。某国际化产品做得漂亮:错误响应带 locale 参数,后端直接返回翻译后的文案,前端零成本。
但别走另一个极端:把堆栈 trace 直接抛给前端。这等于告诉攻击者你的技术栈和代码结构。平衡点是内部错误码+用户友好文案+日志关联 ID,三方各取所需。
坑七:把 API 设计当成"后端自己的事"
这是最深层的问题。很多团队的分工是:后端定接口,前端适配。结果是接口设计只考虑数据库表结构,不考虑 UI 的加载顺序、状态流转、缓存策略。
一个典型场景:商品详情页需要基础信息、价格、库存、评价、推荐商品。后端给了五个独立接口,每个 50ms,前端串行调用就是 250ms,并行调用要处理五个 loading 状态。其实应该有一个聚合接口,或者至少让 BFF(Backend for Frontend)层来做编排。
BFF 不是技术问题,是组织问题。谁维护 BFF?前端写 Node 还是后端写?接口变更时谁来同步?这些问题不解决,API 设计永远停留在"能跑就行"的层面。
那篇摔键盘的会议纪要最后写了什么?
不是技术规范,是协作流程:每个新接口必须前端负责人签字,重大变更提前两周通知,每月一次"接口债务"复盘。三个月后,他们的 API 投诉工单下降了 60%。
但最讽刺的反馈来自一位老后端:「我现在设计接口会先想,这个字段前端怎么用。以前觉得这是浪费时间,现在发现想清楚了,返工更少。」
REST 本身没罪,罪的是把它当成"数据库 HTTP 化"的偷懒。你的团队接口文档最近一次被前端主动点赞,是什么时候?
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.