资源与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这些到底该怎么用,以及幂等性的问题。到时候见。