← 返回博客

API 设计中 UUID 的使用指南

2026-04-14 · 5 分钟阅读

为什么在 API 中使用 UUID

在 REST API 设计中,UUID 作为资源标识符比自增整数 ID 有明显优势:安全性更高——不暴露数据库记录数量和顺序,防止用户通过 /users/1、/users/2 枚举资源;支持客户端生成 ID(幂等创建)——客户端可以在发送 POST 请求前生成 UUID,使创建操作幂等(重试不会创建重复记录);分布式友好——多个服务、多个节点可以独立生成 ID,无冲突;URL 语义更清晰——UUID 在 URL 中一目了然地标识一个特定资源,而不是一个可能变化的序号。

REST API 路径设计规范

# 推荐的 UUID 路径格式(小写,带连字符)
GET    /api/v1/users/550e8400-e29b-41d4-a716-446655440000
PUT    /api/v1/users/550e8400-e29b-41d4-a716-446655440000
DELETE /api/v1/users/550e8400-e29b-41d4-a716-446655440000

# 嵌套资源
GET    /api/v1/users/550e8400-e29b-41d4-a716-446655440000/orders
GET    /api/v1/orders/9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d

# 幂等创建(客户端提供 UUID)
PUT    /api/v1/resources/client-generated-uuid
# 服务端如果已存在该 UUID,可以返回 200 或 409

# API 响应格式规范(统一使用字符串)
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "Example User",
  "createdAt": "2025-01-01T00:00:00Z"
}

格式规范化建议

在 API 中关于 UUID 格式的建议:接受格式宽松(输入时同时接受大小写、带或不带花括号),输出格式严格(始终以小写无花括号格式返回);在路径参数中,对格式进行验证,格式不合法时返回 400 Bad Request 而不是 404 Not Found(告知调用方是格式问题而非"资源不存在");在 JSON body 中,UUID 以字符串形式传输(不要尝试用数字表示,会丢失精度);API 文档中明确说明使用 UUID v4,并提供格式示例。

错误处理最佳实践

# FastAPI 示例:UUID 参数验证和错误处理
from uuid import UUID
from fastapi import FastAPI, HTTPException, Path
from fastapi.responses import JSONResponse

app = FastAPI()

@app.exception_handler(ValueError)
async def uuid_exception_handler(request, exc):
    return JSONResponse(
        status_code=400,
        content={
            "error": "invalid_uuid",
            "message": "The provided ID is not a valid UUID format",
            "expected": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
        }
    )

@app.get("/api/users/{user_id}")
async def get_user(
    user_id: UUID = Path(..., description="User UUID in RFC 4122 format")
):
    user = db.find_user(user_id)
    if not user:
        raise HTTPException(
            status_code=404,
            detail={
                "error": "not_found",
                "message": f"User {user_id} not found"
            }
        )
    return user

幂等性与重试安全

UUID 在 API 幂等性设计中非常有用。标准模式:客户端在首次请求前生成一个 UUID 作为 Idempotency-Key 请求头,服务端将此 UUID 与请求结果缓存(通常在 Redis 中保存 24 小时);如果相同的 Idempotency-Key 再次请求,直接返回缓存的响应,而不重复执行操作;这对于支付、订单创建等不能重复执行的操作特别重要。Stripe、Adyen 等支付平台都使用这种模式,允许客户端安全重试失败的请求而不产生副作用。

# 幂等 API 调用示例
import uuid
import httpx

def create_order_idempotent(order_data, max_retries=3):
    idempotency_key = str(uuid.uuid4())  # 本次调用全局唯一

    for attempt in range(max_retries):
        try:
            response = httpx.post(
                '/api/orders',
                json=order_data,
                headers={'Idempotency-Key': idempotency_key}
            )
            if response.status_code in (200, 201):
                return response.json()
            elif response.status_code == 409:
                return response.json()  # 幂等:返回已存在的结果
        except httpx.NetworkError:
            if attempt == max_retries - 1:
                raise
            # 使用相同的 idempotency_key 重试

GraphQL 中的 UUID 使用

# GraphQL schema 示例
scalar UUID

type User {
  id: UUID!          # 非空 UUID 类型
  name: String!
  email: String!
  posts: [Post!]!
}

type Query {
  user(id: UUID!): User
  users: [User!]!
}

type Mutation {
  createUser(input: CreateUserInput!): User!
  updateUser(id: UUID!, input: UpdateUserInput!): User!
  deleteUser(id: UUID!): Boolean!
}

# 查询示例
query {
  user(id: "550e8400-e29b-41d4-a716-446655440000") {
    id
    name
    email
  }
}

立即免费使用相关工具

免费使用 →