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
📝 发现内容有误?点击此处直接编辑