544_设计RESTful API系统
一句话说明
RESTful API 是以资源为中心、用 HTTP 动词操作的接口规范,是现代 Web 后端开发的基础。
核心知识点
REST 六大约束
- 无状态:每次请求包含所有信息,服务器不保存会话
- 统一接口:标准 HTTP 方法(GET/POST/PUT/DELETE/PATCH)
- 客户端-服务器分离:前后端解耦
- 可缓存性:GET 请求结果可缓存
- 分层系统:客户端不感知中间层(LB/CDN)
- 按需代码(可选):服务器可传送可执行代码
HTTP 动词语义
| 动词 | 含义 | 幂等 | 安全 |
|---|
| GET | 获取资源 | 是 | 是 |
| POST | 创建资源 | 否 | 否 |
| PUT | 全量更新 | 是 | 否 |
| PATCH | 部分更新 | 否 | 否 |
| DELETE | 删除资源 | 是 | 否 |
HTTP 状态码速记
2xx 成功
200 OK - 通用成功
201 Created - 创建成功
204 No Content - 删除成功(无响应体)
4xx 客户端错误
400 Bad Request - 参数错误
401 Unauthorized - 未登录
403 Forbidden - 无权限(已登录但无权)
404 Not Found - 资源不存在
409 Conflict - 冲突(重复创建)
422 Unprocessable - 参数格式正确但业务校验失败
429 Too Many Req - 限流
5xx 服务端错误
500 Internal Error - 服务器内部错误
502 Bad Gateway - 上游服务不可用
503 Unavailable - 服务暂时不可用
实战代码/设计图/模板
生信 API 设计示例(样本管理)
资源:/samples
操作示例:
GET /samples - 列出所有样本(支持分页/过滤)
POST /samples - 创建新样本
GET /samples/{id} - 获取单个样本
PUT /samples/{id} - 全量更新样本
PATCH /samples/{id} - 部分更新(如修改状态)
DELETE /samples/{id} - 删除样本
子资源:
GET /samples/{id}/files - 获取样本下所有文件
POST /samples/{id}/jobs - 提交分析任务
FastAPI 实现示例
from fastapi import FastAPI, HTTPException, Query
from pydantic import BaseModel
from typing import Optional
import uvicorn
app = FastAPI(title="生信平台 API", version="1.0")
class Sample(BaseModel):
name: str
project_id: str
tissue_type: Optional[str] = None
description: Optional[str] = None
class SampleResponse(Sample):
id: str
status: str
created_at: str
# GET 列表(支持分页和过滤)
@app.get("/api/v1/samples", response_model=dict)
async def list_samples(
project_id: Optional[str] = None,
status: Optional[str] = None,
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100)
):
"""
获取样本列表
- 支持按项目ID、状态过滤
- 支持分页
"""
offset = (page - 1) * page_size
samples, total = db.get_samples(
project_id=project_id,
status=status,
offset=offset,
limit=page_size
)
return {
"data": samples,
"meta": {
"total": total,
"page": page,
"page_size": page_size,
"total_pages": (total + page_size - 1) // page_size
}
}
# POST 创建
@app.post("/api/v1/samples", status_code=201)
async def create_sample(sample: Sample):
"""创建新样本"""
# 校验项目是否存在
if not db.project_exists(sample.project_id):
raise HTTPException(status_code=404, detail="项目不存在")
new_sample = db.create_sample(sample.dict())
return {"data": new_sample, "message": "创建成功"}
# PATCH 部分更新
@app.patch("/api/v1/samples/{sample_id}")
async def update_sample(sample_id: str, updates: dict):
"""部分更新样本信息"""
sample = db.get_sample(sample_id)
if not sample:
raise HTTPException(status_code=404, detail="样本不存在")
updated = db.update_sample(sample_id, updates)
return {"data": updated}
# 统一错误响应格式
@app.exception_handler(HTTPException)
async def http_exception_handler(request, exc):
return {
"success": False,
"error": {
"code": exc.status_code,
"message": exc.detail
}
}
API 版本控制策略
方案1: URL版本(推荐)
/api/v1/samples
/api/v2/samples
方案2: Header版本
Accept: application/vnd.myapi.v2+json
方案3: Query参数
/api/samples?version=2
推荐:URL版本,直观且易于调试
面试常问点
| 问题 | 参考答案 |
|---|
| PUT vs PATCH 的区别? | PUT全量替换,PATCH部分更新 |
| 如何设计分页? | cursor分页 vs offset分页 |
| API认证方式有哪些? | JWT/OAuth2/API Key/Session |
| 如何限流? | 令牌桶/漏桶,Redis计数 |
| 接口幂等性怎么实现? | 唯一请求ID,数据库唯一约束 |
速查表
URL设计规范:
用名词不用动词:/samples 而非 /getSamples
用复数:/samples 而非 /sample
层级不超过3层:/projects/{id}/samples/{id}
过滤用Query参数:?status=active&page=1
响应体标准格式:
成功:{"data": {...}, "meta": {...}}
错误:{"error": {"code": 400, "message": "..."}}
列表:{"data": [...], "meta": {"total": 100, "page": 1}}
常用工具:
文档:Swagger/OpenAPI 3.0
测试:Postman / httpie / curl
Mock:Mockoon / WireMock