资源与URI设计:定义、命名规范与版本控制
好,咱们今天聊聊RESTful API里最基础、也最容易踩坑的部分——资源与URI设计。
说实话,我见过太多API设计,接口路径写得五花八门。有的用动词,有的用单数,有的干脆中英文混着来。嗯,这其实挺要命的。因为URI是API的门面,用户第一眼看到的就是它。设计得好,别人用着舒服;设计得烂,人家可能直接就不想用了。
一、什么是资源?
先搞清楚一个概念:资源(Resource)。
说白了,资源就是你要暴露给外界的数据实体。比如用户、订单、文章、商品……这些都是资源。在RESTful架构里,每个资源都有一个唯一的标识符,也就是URI。
我个人习惯把资源分成两类:
- 单一资源:比如一个用户、一篇博客
- 集合资源:比如所有用户、所有博客
举个例子:
# 集合资源
/users # 所有用户
/articles # 所有文章
# 单一资源
/users/123 # ID为123的用户
/articles/abc # ID为abc的文章
这里有个关键点:资源是名词,不是动词。你想想看,如果接口写成 /getUser 或 /createOrder,那跟RPC有什么区别?RESTful的核心思想是“用HTTP动词操作资源”,而不是“在URI里写动作”。
核心原则:URI只描述资源,不描述操作。操作由HTTP方法(GET、POST、PUT、DELETE)来表达。
二、URI命名规范
这部分我踩过不少坑。以前有个项目,团队里每个人写URI的风格都不一样,后来维护起来简直想哭。所以,我总结了一套比较通用的规范,分享给你。
1. 使用名词复数
为什么用复数?因为集合资源天然就是多个对象的集合。比如 /users 表示“所有用户”,语义清晰。而 /user 容易让人困惑:这是单个用户还是集合?
当然,也有例外。比如获取当前登录用户的信息,有人会用 /me 或 /current-user。这个我个人觉得可以接受,但尽量保持一致性。
# 推荐
GET /users # 获取用户列表
GET /users/123 # 获取单个用户
POST /users # 创建用户
# 不推荐
GET /user # 是单个还是集合?
GET /getUser # 动词混进来了
POST /createUser # 同上
2. 全部小写
这个其实没什么好争论的。大小写混用会导致很多问题,尤其是在Linux服务器上,路径是区分大小写的。你想想看,用户如果记错了大小写,直接返回404,体验多差。
我在项目中遇到过一件事:有个同事把接口写成 /Users/123,结果前端调用时写成了 /users/123,排查了半天才发现是大小写问题。从那以后,我们团队就强制要求所有URI必须小写。
3. 使用连字符(-)代替下划线(_)
为什么用连字符?因为连字符在URL里更友好。下划线在某些浏览器或搜索引擎里可能会被截断或误解。而且,连字符更符合英文阅读习惯。
# 推荐
GET /order-items
GET /user-profiles
# 不推荐
GET /order_items
GET /userProfiles # 驼峰也不行
小技巧:如果资源名称由多个单词组成,用连字符连接。比如 /blog-posts 比 /blogposts 可读性强得多。
三、版本控制策略
API版本控制是个老生常谈的话题。你想想看,你的API不可能一成不变。业务在变,需求在变,接口也得跟着变。但你不能让老用户突然用不了,对吧?所以版本控制就很重要了。
常见的版本控制策略有三种:URL路径、请求头、请求参数。我分别说说它们的优缺点。
1. URL路径版本控制
这是最直观、最常用的方式。直接把版本号写在URI里。
GET /v1/users
GET /v2/users
优点:
- 一目了然,用户一眼就知道用的是哪个版本
- 容易缓存和路由
- 调试方便,直接改URL就能切换版本
缺点:
- URI变得冗长
- 版本号一旦发布,就很难再改
我个人比较推荐这种方式。为什么呢?因为简单、直接、不容易出错。我曾经在一个项目里用过请求头版本控制,结果前端同事老是忘记传版本号,导致各种奇怪的问题。后来改成URL路径,问题就少了很多。
2. 请求头版本控制
通过自定义请求头来指定版本号。
GET /users
Headers: Accept: application/vnd.myapp.v1+json
优点:
- URI保持干净,没有版本号污染
- 符合RESTful的“资源标识符不变”原则
缺点:
- 用户看不到版本号,调试不方便
- 需要额外的文档说明
- 浏览器或工具可能不支持自定义请求头
注意:如果你选择请求头版本控制,一定要在文档里写清楚。我曾经见过一个团队,用了请求头版本控制,但文档里只字未提,结果新来的开发调了三天接口都没调通。
3. 请求参数版本控制
通过查询参数来指定版本号。
GET /users?version=1
GET /users?version=2
优点:
- 实现简单,不需要改路由
- 容易测试
缺点:
- 参数容易被忽略或忘记
- 缓存策略复杂
- URL看起来不够优雅
说实话,这种方式我不太推荐。为什么呢?因为版本号是资源标识的一部分,放在参数里显得不够正式。而且,如果用户不传版本号,你怎么办?默认用最新版?那老用户可能就崩了。
三种方式对比
| 策略 | 优点 | 缺点 | 推荐度 |
|---|---|---|---|
| URL路径 | 直观、易缓存、易调试 | URI变长 | ⭐⭐⭐⭐⭐ |
| 请求头 | URI干净、符合RESTful | 调试不便、需文档 | ⭐⭐⭐⭐ |
| 请求参数 | 实现简单 | 易遗漏、缓存复杂 | ⭐⭐⭐ |
四、避坑指南
最后,分享几个我踩过的坑,希望能帮你少走弯路。
- 不要用动词:
/getUser、/deleteOrder这种写法,一看就是新手。记住,URI只描述资源,操作交给HTTP方法。 - 不要用文件扩展名:比如
/users.json、/users.xml。RESTful API应该通过请求头(Accept)来协商返回格式,而不是在URI里写死。 - 不要过度嵌套:比如
/users/123/orders/456/items/789。嵌套层级太深,URI会变得又长又难读。一般来说,嵌套不超过两层。 - 版本号要尽早定:我曾经有个项目,一开始没加版本号,后来用户多了,想加版本号发现已经来不及了。所以,哪怕你刚开始只有v1,也建议在URI里带上。
总结一下:资源用名词复数、URI全小写、单词用连字符、版本控制推荐URL路径。这些规范看起来简单,但坚持做下来,你的API会变得清晰、易用、好维护。
嗯,今天就聊到这儿。下一章咱们聊聊HTTP方法的使用,包括GET、POST、PUT、DELETE这些到底该怎么用,以及幂等性的问题。到时候见。