2、RESTful API 设计原则
好,咱们直接进入正题。这一章聊的是 API 设计,说白了就是怎么让你的接口看起来「专业」。我见过太多项目,接口命名随心所欲,状态码乱用一气,最后维护起来简直是一场噩梦。今天咱们就把这些坑一个个填上。
什么是 REST?
REST 全称是 Representational State Transfer,翻译过来叫「表述性状态转移」。名字挺拗口,但核心思想很简单:把后端资源通过 URL 暴露出来,然后用 HTTP 动词去操作它们。
举个例子。你有一个用户系统,传统做法可能是这样:
GET /getUserById?id=123
POST /createUser
POST /deleteUser?id=456
这种写法有什么问题?说白了就是「动词」混在 URL 里,语义混乱。RESTful 的做法是:
GET /users/123 → 获取用户
POST /users → 创建用户
DELETE /users/456 → 删除用户
你看,资源是 /users,操作交给 HTTP 动词。这样一眼就能看懂。
核心要点:REST 不是协议,而是一种架构风格。它利用 HTTP 本身的能力,让 API 更清晰、更可预测。
资源命名规范
命名这件事,我踩过不少坑。早期有个项目,接口命名五花八门,有人用 /getAllUsers,有人用 /userList,还有人用 /queryUser。后来重构时,我定了一套规范,从此清爽多了。
几个关键原则:
- 使用名词复数:
/users而不是/user,/orders而不是/order - 小写字母 + 连字符:
/order-items而不是/orderItems或/order_items - 层级关系用斜杠:
/users/123/orders表示「用户 123 的订单」 - 避免动词:不要出现
/createUser,用POST /users代替
举个例子,一个博客系统的资源命名:
GET /articles → 文章列表
GET /articles/42 → 单篇文章
POST /articles → 创建文章
PUT /articles/42 → 更新文章
DELETE /articles/42 → 删除文章
GET /articles/42/comments → 文章的评论列表
个人经验:我习惯在 URL 中避免使用文件扩展名,比如 /users.json 这种。用 Accept 头来控制返回格式更优雅。
HTTP 动词与状态码
这部分是面试高频题,也是实际开发中最容易出问题的地方。咱们一个一个说。
常用 HTTP 动词
| 动词 | 用途 | 是否幂等 | 请求体 |
|---|---|---|---|
| GET | 获取资源 | 是 | 无 |
| POST | 创建资源 | 否 | 有 |
| PUT | 全量更新 | 是 | 有 |
| PATCH | 部分更新 | 否 | 有 |
| DELETE | 删除资源 | 是 | 可选 |
这里有个容易混淆的点:PUT 和 PATCH 的区别。PUT 是整体替换,PATCH 是局部修改。举个例子:
// 用户原始数据
{ "name": "张三", "age": 25, "email": "zhang@example.com" }
// PUT /users/123 只传 { "name": "李四" }
// 结果:{ "name": "李四" } —— age 和 email 丢了!
// PATCH /users/123 只传 { "name": "李四" }
// 结果:{ "name": "李四", "age": 25, "email": "zhang@example.com" }
嗯,这里要注意:PUT 要求客户端传完整资源,否则会覆盖掉其他字段。我曾经见过一个项目,前端用 PUT 只传了修改的字段,结果用户的其他信息全没了……那场面,真是惨不忍睹。
状态码选择
状态码用对了,接口的「自解释性」会大大提升。我总结了一套常用组合:
- 200 OK:GET 成功、PUT 成功、PATCH 成功
- 201 Created:POST 创建成功,记得在响应头里带上
Location - 204 No Content:DELETE 成功,或者某些不需要返回体的操作
- 400 Bad Request:参数校验失败,客户端的问题
- 401 Unauthorized:未认证,比如没带 token
- 403 Forbidden:已认证但没权限
- 404 Not Found:资源不存在
- 409 Conflict:资源冲突,比如重复创建
- 422 Unprocessable Entity:语义错误,比如邮箱格式不对
- 500 Internal Server Error:服务端异常,别乱用
避坑指南:我曾经见过有人把所有错误都返回 400,包括服务器内部异常。这会让前端非常困惑——到底是我的参数错了,还是你们服务器炸了?区分 4xx 和 5xx 是基本素养。
API 版本控制策略
版本控制这事儿,说白了就是「如何优雅地让新旧接口共存」。我见过最粗暴的做法是在 URL 里加版本号,比如 /v1/users、/v2/users。这方法简单直接,但有个问题:URL 变得冗长,而且版本号会「污染」资源路径。
常见的几种策略:
| 策略 | 示例 | 优点 | 缺点 |
|---|---|---|---|
| URL 路径 | /v1/users |
简单直观,容易实现 | URL 变长,版本号与资源耦合 |
| 请求头 | Accept: application/vnd.myapp.v1+json |
URL 干净,符合 REST 理念 | 调试不方便,浏览器直接访问困难 |
| 查询参数 | /users?version=1 |
灵活,可动态切换 | 容易被缓存污染,语义不清晰 |
我个人习惯用 URL 路径版本控制。为什么?因为简单。你想想看,一个团队里不是所有人都理解 REST 的深层理念。用 URL 路径,谁都能一眼看出这是哪个版本的接口。而且调试的时候,直接在浏览器里改个数字就能测试,多方便。
不过要注意一点:不要频繁升级版本号。我见过一个项目,半年内从 v1 升到了 v5,每次都是小改动。这其实没必要。版本号应该对应「不兼容的变更」,比如字段名改了、接口语义变了。如果是新增字段,完全可以在原版本上扩展,客户端忽略未知字段即可。
我的建议:在 .NET 中实现版本控制,可以用 Microsoft.AspNetCore.Mvc.Versioning 这个包。它支持 URL 路径、请求头、查询参数等多种方式,而且能自动生成 API 文档。我在几个生产项目中都用过,稳定可靠。
最后说一句:API 设计没有银弹。RESTful 原则是指导,不是教条。根据你的业务场景灵活调整,才是正道。比如有些内部系统,团队就几个人,用 /getUser 这种命名也完全没问题。但如果你在做开放 API,那还是老老实实遵循规范吧。