Python 后端基础(二):RESTful API 设计规范,接口命名、状态码和返回格式怎么写
RESTful API 不是复杂理论,而是一套让接口更清晰、更容易维护的设计习惯。本文用 Python 后端项目的例子讲清资源、URL、HTTP 方法、状态码、分页、筛选和统一返回格式。
【一、为什么要学 RESTful API】
后端项目里最容易乱的地方就是接口。
如果没有规范,接口可能会写成这样:
/getUser
/createNewUser
/delete_user_by_id
/queryAllUserData
/doLogin
短期能跑,长期会很难维护。前端不知道接口语义,测试不知道怎么覆盖,新人接手也很痛苦。
RESTful API 的目的就是让接口围绕“资源”设计,而不是围绕“动作”乱起名。
【二、什么是资源】
资源就是系统里的业务对象,比如:
- 用户:`users`
- 文章:`articles`
- 商品:`products`
- 订单:`orders`
- 知识库:`knowledge-bases`
- 文档:`documents`
RESTful 推荐 URL 使用名词,而不是动词。
推荐:/users/1
不推荐:/getUserById?id=1
因为 HTTP 方法已经表达了动作,URL 只需要表达资源。
【三、HTTP 方法和 URL 怎么配合】
以用户资源为例:
| 操作 | 方法 | URL | 含义 |
|---|---|---|---|
| 查询用户列表 | GET | `/users` | 查询多个用户 |
| 查询单个用户 | GET | `/users/1` | 查询 id=1 的用户 |
| 创建用户 | POST | `/users` | 新增用户 |
| 全量更新用户 | PUT | `/users/1` | 覆盖用户数据 |
| 部分更新用户 | PATCH | `/users/1` | 修改部分字段 |
| 删除用户 | DELETE | `/users/1` | 删除用户 |
这就是 RESTful 最常用的设计方式。
【四、嵌套资源怎么设计】
如果一个资源属于另一个资源,可以使用嵌套路径。
比如某个知识库下的文档:
GET /knowledge-bases/10/documents
POST /knowledge-bases/10/documents
GET /knowledge-bases/10/documents/3
意思很清楚:操作的是 id=10 的知识库下面的文档。
但嵌套不要太深。一般最多两到三层,太深说明接口设计可能过度复杂。
【五、查询、分页、排序怎么写】
查询参数适合放在 URL 的 query string 里。
GET /articles?keyword=python&page=1&page_size=20
GET /orders?status=paid&sort=-created_at
常见参数:
- `page`:第几页
- `page_size`:每页多少条
- `keyword`:关键词搜索
- `status`:按状态筛选
- `sort`:排序字段
返回时最好带上分页信息:
```json
{
"items": [],
"total": 125,
"page": 1,
"page_size": 20
}
```
【六、状态码怎么设计】
RESTful API 不要求你背所有状态码,但常用的要用对。
| 状态码 | 场景 |
|---|---|
| 200 | 查询或普通操作成功 |
| 201 | 创建资源成功 |
| 204 | 删除成功且不返回内容 |
| 400 | 参数错误 |
| 401 | 未登录或 token 无效 |
| 403 | 没有权限 |
| 404 | 资源不存在 |
| 409 | 数据冲突,比如邮箱已注册 |
| 422 | 参数格式校验失败 |
| 500 | 服务端异常 |
新手常见问题是:所有请求都返回 200,然后在 JSON 里写 `success=false`。这样不是完全不能用,但会削弱 HTTP 状态码本身的语义。
【七、统一返回格式怎么设计】
项目里最好统一响应结构,否则前端处理会很麻烦。
一种常见格式:
```json
{
"code": 0,
"message": "success",
"data": {}
}
```
分页接口
```json
{
"code": 0,
"message": "success",
"data": {
"items": [],
"total": 100,
"page": 1,
"page_size": 20
}
}
```
错误响
```json
{
"code": 1001,
"message": "用户名或密码错误",
"data": null
}
```
注意:状态码和业务 code 可以同时存在。状态码表达 HTTP 层面的成功失败,业务 code 表达更细的业务错误。
【八、FastAPI 示例】
```python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class UserCreate(BaseModel):
username: str
email: str
fake_users = {}
@app.post("/users", status_code=201)
def create_user(user: UserCreate):
user_id = len(fake_users) + 1
fake_users[user_id] = user.model_dump()
return {"id": user_id, **fake_users[user_id]}
@app.get("/users/{user_id}")
def get_user(user_id: int):
if user_id not in fake_users:
raise HTTPException(status_code=404, detail="用户不存在")
return fake_users[user_id]
```
这个例子体现了:
- POST `/users` 创建用户。
- GET `/users/{user_id}` 查询用户。
- 不存在时返回 404。
- 请求体用 Pydantic 校验。
【九、常见坑】
- URL 里混入动词:`/getUser`、`/deleteUser`。
- 单复数混乱:有的写 `/user`,有的写 `/users`。
- 查询接口使用 POST,导致语义混乱。
- 错误码没有统一规则,前端只能靠猜。
- 分页没有总数,前端无法做分页器。
- 删除接口真的物理删除,但业务上需要保留审计记录。
【十、面试高频问题】
【1. RESTful API 的核心思想是什么?】
核心思想是把系统中的对象抽象成资源,用 URL 表示资源,用 HTTP 方法表示对资源的操作。这样接口语义清晰,便于前后端协作和维护。
【2. PUT 和 PATCH 有什么区别?】
PUT 通常表示全量更新,即客户端提交完整资源;PATCH 表示部分更新,只提交要修改的字段。比如用户有 username、email、avatar,PATCH 可以只改 avatar。
【3. 为什么要做统一响应格式?】
统一响应格式可以降低前端处理成本,也方便统一错误处理、日志记录和接口文档生成。否则不同接口返回结构不同,前端每接一个接口都要写特殊逻辑。
