API设计入门:从URL到错误码的规范心法

很多开发者第一次写接口时的想法是:“功能能跑就行,反正就我自己调用。”但现实是:你写的接口迟早会被前端同事、其他团队甚至第三方使用,而接口一旦上线,任何不兼容的改动都要付出代价。可以说,接口是系统的“公共门面”,设计成本是后置的:前期省下的思考时间,后期会连本带利还回去。好的API设计不需要多深的理论,掌握几条核心规范,就能让接口好用、稳定、少扯皮。

设计原则:URL描述资源,动词表达操作

REST风格的第一要义是分清“资源”与“操作”。URL只用来定位资源,用名词复数形式,例如 /users、/orders/123;操作交给HTTP方法:GET查询、POST创建、PUT整体更新、DELETE删除、PATCH局部更新。反例是“动词化URL”:/getUser、/createOrder,它们把操作塞进路径,接口会越写越乱。资源间的关系用层级表达:/users/123/orders 表示“用户123的订单”;过滤、排序、分页等修饰条件放查询参数:/orders?status=paid&page=2&limit=20。版本信息放进路径前缀:/v1/orders,为将来的不兼容升级留好退路。

状态码与错误信息:让调用方“看得懂”

HTTP状态码是现成的语义词典:200成功、201创建成功、400参数错误、401未认证、403无权限、404不存在、422校验失败、429请求过频、500服务器错误。很多团队偷懒把所有情况都返回200,再在响应体里塞业务code——不是不行,但一定要全项目统一,最忌讳“有时候靠状态码、有时候靠业务码”的混用。错误响应建议固定结构:code给程序判断,message给人看,details给排查线索。注意错误信息不要泄露内部细节(堆栈、SQL语句),对用户友好,对攻击者保密。

三个容易被忽略的细节

第一,幂等性。网络超时后客户端重试是常态。GET天然幂等,DELETE也建议幂等;POST创建不幂等——所以“提交订单”这类接口要支持客户端传入幂等键(Idempotency-Key),防止用户点一次“支付”被扣两次款。

第二,分页的完整性。列表接口别只回一页数据,要同时返回总数:{“items”: […], “total”: 342, “page”: 2, “limit”: 20}。数据量大的场景(如订单流)优先用游标分页,避免深翻页的性能灾难。

第三,契约先行。前后端并行开发时,先定义好接口文档(OpenAPI/Swagger)再各自开发,而不是后端写完扔给前端“自己看代码”。文档即契约,还能自动生成调试页面,是减少联调扯皮最有效的一步。

案例:一次接口事故的教训

某团队上线了一个订单查询接口,字段名叫 amount,后来业务加了币种,开发直接把字段改名成 amount_cny 并上线——前端没接到通知,线上页面金额全部显示为空,紧急回滚才恢复。复盘发现三个问题:接口没有版本化,改动直接覆盖旧契约;没走文档更新流程;字段语义变化本应新增字段或开新版本,而不是改名。此后团队立下规矩:破坏性变更必须开新版本(/v2),旧版本保留过渡期;新增字段只加不改;所有变更先更新文档再动代码。此后这个团队再没因接口变更出过线上事故。

常见误区

  • 动词化URL:/getUserList、/deleteOrder 短期顺手,长期让接口语义混乱,也不利于网关统一做权限控制。
  • 状态码与业务码混用:风格不统一是接口最大的隐性成本,团队必须选定一种并写进规范。
  • 无视幂等直接上线:涉及支付、下单、创建的接口不做幂等,一次超时重试就可能造成重复扣款、重复下单。
  • 直接改旧字段、删旧参数:你眼里是“清理”,调用方眼里是“事故”。破坏性变更走新版本,旧版本给足迁移期。
  • 不写文档:代码即文档只对维护者成立,对调用方不成立。接口文档不是可选项,是交付物的一部分。

行动建议

  • 盘点你负责的接口,找出动词化URL和字段语义不清的地方,列入重构清单。
  • 为团队定一页纸的API规范:命名、状态码、错误结构、分页、版本策略,先立规矩再谈完善。
  • 涉及资金、订单、创建的接口,本周内补上幂等设计并自查重试逻辑。
  • 如果项目还没有OpenAPI文档,先用工具从现有代码生成一份,从“补文档”开始建立契约意识。

标签:#, #