3. RESTful API设计原则:资源命名规范、HTTP动词使用、状态码含义、API版本管理

好,咱们今天聊点实在的。RESTful API,说白了就是一套让客户端和服务器好好说话的规矩。我做了这么多年桌面应用,跟后端API打交道的时间比跟UI打交道还多。你想想看,一个桌面应用再漂亮,如果网络请求写得一塌糊涂,用户照样摔键盘。

这一章,我就把RESTful API设计里最核心的几个点掰开揉碎讲给你听。资源怎么命名、动词怎么用、状态码怎么选、版本怎么管——这些都是我踩过坑之后总结出来的经验。

3.1 资源命名规范:让URL像文件路径一样清晰

资源命名,是RESTful API的门面。我见过太多乱七八糟的URL了,比如 /getUserInfo/delete_order/api/v1/updateProduct。嗯,这些写法不能说错,但离「优雅」差得远。

核心原则就一条:用名词,不用动词。

为什么?因为HTTP动词已经表达了操作意图。你再用动词就是画蛇添足。我个人习惯把URL当作「资源地址」,而不是「操作指令」。

✅ 推荐写法:

  • /users — 用户列表
  • /users/123 — 某个用户
  • /orders/456/items — 某个订单下的商品

❌ 不推荐写法:

  • /getUsers
  • /deleteUserById
  • /api/updateOrderStatus

还有几个小细节要注意:

  • 用复数名词/users 而不是 /user。统一用复数,省得纠结。
  • 用连字符分隔单词/order-items 而不是 /orderItems/order_items。URL不区分大小写,下划线在某些浏览器里会被吞掉。
  • 层级关系用斜杠表示/users/123/orders 表示「用户123的订单」。不要搞成 /orders?userId=123,除非是过滤条件。

我的小技巧: 设计URL时,想象你在操作一个文件系统。每个资源就是一个文件夹或文件,路径越短越好,但信息要完整。

3.2 HTTP动词使用:GET/POST/PUT/DELETE 各司其职

HTTP动词,就是你对资源做的「操作」。很多新手喜欢只用GET和POST,其他动词一概不用。我在项目中遇到过这种情况:一个团队用POST干所有事,结果API文档写得比小说还长。

其实就四个动词,记清楚就行:

动词 作用 幂等 示例
GET 获取资源 GET /users/123
POST 创建资源 POST /users
PUT 完整更新资源 PUT /users/123
DELETE 删除资源 DELETE /users/123

这里有个坑: PUT和PATCH的区别。PUT是「全量替换」,你传什么字段,资源就变成什么字段。PATCH是「局部更新」,只改你传的字段。我个人习惯:如果更新字段少,用PATCH;如果整个对象都变了,用PUT。

⚠️ 注意: POST不是幂等的。你调两次POST,会创建两个资源。而GET、PUT、DELETE调多少次结果都一样。这个特性在重试机制里特别重要——我曾经因为没注意幂等性,导致用户下了两笔重复订单,被产品经理追着骂了一下午。

3.3 状态码含义:让客户端一眼看懂发生了什么

状态码,是服务器给客户端的「表情」。你返回200,客户端就知道一切正常;返回404,客户端就知道资源没找到。但很多人只用了200和500,其他状态码一概不用。这就像你跟人说话,只会说「好」和「不好」,太粗糙了。

我建议至少掌握这几类:

  • 2xx 成功
    • 200 OK — GET、PUT、PATCH成功
    • 201 Created — POST创建成功,记得在响应头里返回 Location 指向新资源
    • 204 No Content — DELETE成功,不需要返回内容
  • 4xx 客户端错误
    • 400 Bad Request — 参数格式不对,比如JSON解析失败
    • 401 Unauthorized — 没登录或token过期
    • 403 Forbidden — 登录了但没权限
    • 404 Not Found — 资源不存在
    • 409 Conflict — 资源冲突,比如重复创建
    • 422 Unprocessable Entity — 参数校验失败,比如邮箱格式不对
  • 5xx 服务端错误
    • 500 Internal Server Error — 服务器炸了,别让用户看到堆栈信息
    • 502 Bad Gateway — 上游服务挂了
    • 503 Service Unavailable — 服务过载或维护中

避坑指南: 我曾经见过一个API,不管什么错误都返回200,然后在响应体里放一个 {"code": -1, "message": "xxx"}。这种做法让客户端代码变得极其丑陋——每次请求都要先解析响应体,再判断业务状态码。记住:HTTP状态码是给协议层用的,业务状态码是给应用层用的,别混在一起。

3.4 API版本管理:别让旧客户端崩掉

API版本管理,说白了就是「向前兼容」。你升级了API,不能把老版本的客户端搞崩。我见过最惨的一次:后端改了接口返回格式,没通知前端,结果桌面应用直接白屏,用户数据全丢了。

常见的版本管理方式有三种:

方式 示例 优点 缺点
URL路径 /api/v1/users 简单直观,容易路由 版本号污染URL
请求头 Accept: application/vnd.myapp.v2+json URL干净,符合RESTful 调试麻烦,浏览器不友好
查询参数 /api/users?version=2 实现简单 容易被缓存,语义不清晰

我个人习惯用URL路径方式。虽然有人觉得不够「纯粹」,但胜在直观。你想想看,桌面应用的日志里看到 /api/v2/users,一眼就知道用的是哪个版本,不用去翻请求头。

我的建议: 版本号用整数,别用小数点。比如 v1v2,而不是 v1.1v1.2。为什么?因为小版本号通常意味着向后兼容的改动,没必要暴露给客户端。只有不兼容的改动(比如删字段、改参数类型)才需要升大版本。

还有一点:不要轻易废弃旧版本。桌面应用不像网页,用户不会自动更新。你至少要给用户3-6个月的过渡期,并在响应头里加一个 Deprecation: true 标记,提醒客户端该升级了。

⚠️ 我曾经踩过的坑: 有一次我升级了API,把 /api/v1/users 直接删了,结果有十几个企业客户用的还是旧版桌面应用,第二天客服电话被打爆。从那以后,我定了个规矩:旧版本至少保留6个月,并且在日志里记录每个版本的调用次数,低于1%时才考虑下线。

3.5 总结:设计API时问自己三个问题

好了,这一章的内容差不多就这些。最后送你三个问题,每次设计API时问自己一遍:

  1. 这个URL像不像一个资源? 如果URL里出现了动词,说明设计有问题。
  2. 这个状态码客户端能看懂吗? 别返回200表示错误,也别返回500表示参数不对。
  3. 如果明天改了接口,旧客户端会崩吗? 如果会,赶紧加版本号。

记住:好的API设计,是让调用者觉得「这很自然,本来就应该这样」。而不是让调用者翻着文档骂「这谁设计的破接口」。嗯,咱们做桌面应用的,尤其要注意这一点——用户可没耐心看你的API文档。