全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-19 10 分钟 ✍️ juanwangdev

Python API设计最佳实践

良好的API设计是系统可维护性和可扩展性的基础。

RESTful API核心原则

资源导向设计

Python
# 资源命名规范
# - 使用名词而非动词
# - 使用复数形式
# - 使用层级表达关系

# 正确示例
GET    /users           # 获取用户列表
GET    /users/{id}      # 获取单个用户
POST   /users           # 创建用户
PUT    /users/{id}      # 更新用户
DELETE /users/{id}      # 删除用户

# 子资源
GET    /users/{id}/orders        # 用户订单
GET    /users/{id}/orders/{oid}  # 用户特定订单

# 错误示例(避免)
GET    /getUsers
POST   /createUser
DELETE /deleteUser?id=1

HTTP语义正确使用

Python
from flask import Flask, jsonify, request

app = Flask(__name__)

# GET:获取资源(幂等、安全)
@app.route('/users', methods=['GET'])
def get_users():
    users = query_users(request.args.get('page', 1))
    return jsonify(users)

# POST:创建资源(非幂等)
@app.route('/users', methods=['POST'])
def create_user():
    user = request.get_json()
    created = save_user(user)
    return jsonify(created), 201

# PUT:完整更新资源(幂等)
@app.route('/users/<int:id>', methods=['PUT'])
def update_user(id):
    user = request.get_json()
    updated = replace_user(id, user)
    return jsonify(updated), 200

# PATCH:部分更新资源
@app.route('/users/<int:id>', methods=['PATCH'])
def patch_user(id):
    updates = request.get_json()
    updated = partial_update_user(id, updates)
    return jsonify(updated), 200

# DELETE:删除资源(幂等)
@app.route('/users/<int:id>', methods=['DELETE'])
def delete_user(id):
    remove_user(id)
    return '', 204

状态码规范使用

Python
# 常用HTTP状态码
# 200 OK          - 成功
# 201 Created     - 创建成功
# 204 No Content  - 删除成功(无返回内容)
# 400 Bad Request - 参数错误
# 401 Unauthorized - 未认证
# 403 Forbidden   - 无权限
# 404 Not Found   - 资源不存在
# 409 Conflict    - 资源冲突
# 422 Unprocessable Entity - 参数校验失败
# 500 Internal Server Error - 服务错误

@app.route('/users/<int:id>')
def get_user(id):
    user = find_user(id)
    if user is None:
        return jsonify({'error': 'User not found'}), 404
    return jsonify(user), 200

请求响应设计

统一响应格式

Python
# 成功响应
{
    "data": {
        "id": 1,
        "name": "Alice"
    },
    "meta": {
        "timestamp": "2026-05-19T10:00:00Z"
    }
}

# 列表响应
{
    "data": [...],
    "meta": {
        "total": 100,
        "page": 1,
        "per_page": 20
    }
}

# 错误响应
{
    "error": {
        "code": "USER_NOT_FOUND",
        "message": "User with id 1 not found",
        "details": {...}
    }
}

分页设计

Python
from flask import jsonify, request

def paginate_response(items, total, page, per_page):
    "统一分页响应"
    return jsonify({
        'data': items,
        'meta': {
            'total': total,
            'page': page,
            'per_page': per_page,
            'total_pages': (total + per_page - 1) // per_page
        },
        'links': {
            'self': f'/users?page={page}',
            'next': f'/users?page={page + 1}' if page * per_page < total else None,
            'prev': f'/users?page={page - 1}' if page > 1 else None
        }
    })

@app.route('/users')
def list_users():
    page = int(request.args.get('page', 1))
    per_page = int(request.args.get('per_page', 20))

    users, total = query_users_paginated(page, per_page)
    return paginate_response(users, total, page, per_page)

过滤与排序

Python
@app.route('/users')
def list_users():
    # 过滤参数
    filters = {}
    if request.args.get('status'):
        filters['status'] = request.args['status']
    if request.args.get('role'):
        filters['role'] = request.args['role']

    # 排序参数
    sort_by = request.args.get('sort', 'created_at')
    order = request.args.get('order', 'desc')

    # 搜索参数
    search = request.args.get('q')

    users = query_users(filters=filters, sort=sort_by, order=order, search=search)
    return jsonify({'data': users})

版本管理策略

URL路径版本

Python
# 最常用方式
@app.route('/v1/users')
def v1_users():
    pass

@app.route('/v2/users')
def v2_users():
    pass

# 缺点:URL变化,客户端需修改

请求头版本

Python
@app.route('/users')
def users():
    version = request.headers.get('Accept-Version', 'v1')

    if version == 'v1':
        return jsonify(v1_format_users())
    elif version == 'v2':
        return jsonify(v2_format_users())

# 或使用Accept头
Accept: application/vnd.myapi.v1+json
Accept: application/vnd.myapi.v2+json

版本兼容策略

Python
# 向后兼容的变更(无需新版本)
# - 添加可选字段
# - 添加新端点
# - 添加新可选参数

# 需要新版本的变更
# - 删除字段
# - 修改字段名称
# - 修改响应结构

def v2_user_response(user):
    "v2版本响应格式"
    return {
        'id': user.id,
        'name': user.name,
        'email': user.email,
        'created_at': user.created_at.isoformat(),
        'links': {
            'self': f'/v2/users/{user.id}',
            'orders': f'/v2/users/{user.id}/orders'
        }
    }

错误处理设计

统一错误格式

Python
class APIError(Exception):
    "API统一错误"
    def __init__(self, code, message, status_code=400, details=None):
        self.code = code
        self.message = message
        self.status_code = status_code
        self.details = details

    def to_dict(self):
        return {
            'error': {
                'code': self.code,
                'message': self.message,
                'details': self.details
            }
        }

@app.errorhandler(APIError)
def handle_api_error(error):
    response = jsonify(error.to_dict())
    response.status_code = error.status_code
    return response

# 使用示例
@app.route('/users/<int:id>')
def get_user(id):
    user = find_user(id)
    if user is None:
        raise APIError('USER_NOT_FOUND', f'User {id} not found', 404)
    return jsonify(user)

预定义错误码

Python
ERROR_CODES = {
    'VALIDATION_ERROR': {'message': 'Validation failed', 'status': 400},
    'AUTH_REQUIRED': {'message': 'Authentication required', 'status': 401},
    'PERMISSION_DENIED': {'message': 'Permission denied', 'status': 403},
    'RESOURCE_NOT_FOUND': {'message': 'Resource not found', 'status': 404},
    'RESOURCE_EXISTS': {'message': 'Resource already exists', 'status': 409},
    'INTERNAL_ERROR': {'message': 'Internal server error', 'status': 500},
}

def raise_error(code, details=None):
    info = ERROR_CODES[code]
    raise APIError(code, info['message'], info['status'], details)

认证授权设计

JWT认证

Python
import jwt
from datetime import datetime, timedelta
from functools import wraps

SECRET_KEY = 'your-secret-key'

def generate_token(user_id):
    "生成JWT"
    payload = {
        'user_id': user_id,
        'exp': datetime.utcnow() + timedelta(hours=24),
        'iat': datetime.utcnow()
    }
    return jwt.encode(payload, SECRET_KEY, algorithm='HS256')

def verify_token(token):
    "验证JWT"
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
        return payload['user_id']
    except jwt.ExpiredSignatureError:
        raise APIError('TOKEN_EXPIRED', 'Token has expired', 401)
    except jwt.InvalidTokenError:
        raise APIError('TOKEN_INVALID', 'Invalid token', 401)

def auth_required(f):
    "认证装饰器"
    @wraps(f)
    def decorated(*args, **kwargs):
        token = request.headers.get('Authorization', '').replace('Bearer ', '')
        if not token:
            raise APIError('AUTH_REQUIRED', 'Authentication required', 401)

        user_id = verify_token(token)
        kwargs['current_user_id'] = user_id
        return f(*args, **kwargs)
    return decorated

@app.route('/users/me')
@auth_required
def get_current_user(current_user_id):
    user = find_user(current_user_id)
    return jsonify(user)

权限控制

Python
def role_required(roles):
    "角色权限装饰器"
    def decorator(f):
        @wraps(f)
        @auth_required
        def decorated(*args, **kwargs):
            user_id = kwargs['current_user_id']
            user = find_user(user_id)

            if user.role not in roles:
                raise APIError('PERMISSION_DENIED', 'Permission denied', 403)

            return f(*args, **kwargs)
        return decorated
    return decorator

@app.route('/admin/users')
@role_required(['admin'])
def admin_list_users():
    users = query_all_users()
    return jsonify(users)

API文档设计

OpenAPI规范

Python
# openapi.yaml示例
openapi: 3.0.0
info:
  title: User API
  version: 1.0.0

paths:
  /users:
    get:
      summary: List users
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserList'

components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        email:
          type: string

最佳实践总结

设计原则实践要点
URL设计使用名词复数、层级关系、避免动词
HTTP语义GET查询、POST创建、PUT更新、DELETE删除
响应格式统一结构、包含meta、错误标准化
版本管理URL路径版本最常用、保持向后兼容
认证授权JWT标准、角色权限分离
文档规范OpenAPI标准、自动生成

注意:API设计要先规划好资源和关系,避免后期大规模重构。

要点总结

  • RESTful API资源导向:名词复数、层级表达关系、正确HTTP方法
  • 统一响应格式:data+meta结构,错误码标准化,分页包含链接
  • 版本管理:URL路径版本(/v1/)最常用,向后兼容减少版本迭代
  • 错误处理:统一Error类,预定义错误码,HTTP状态码正确使用
  • 认证授权:JWT令牌标准,装饰器实现权限控制,角色分离
  • API文档:遵循OpenAPI规范,工具自动生成文档

存放路径articles/PYTHON/专家/架构与设计/API设计最佳实践.md

📝 发现内容有误?点击此处直接编辑

← 上一篇 Python虚拟环境管理
下一篇 → Python SOLID原则实践
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库