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,那还是老老实实遵循规范吧。