64 lines
2.4 KiB
Python
64 lines
2.4 KiB
Python
"""
|
||
用户 API 路由模块
|
||
- 使用 Blueprint 实现模块化路由,避免单文件膨胀
|
||
- 包含参数校验、异常处理和分页查询的生产级实现
|
||
- 所有数据库写操作均包裹在 try/except 中,失败时自动回滚
|
||
"""
|
||
from flask import Blueprint, request, jsonify
|
||
from app import db
|
||
from app.models.user import User
|
||
|
||
# 创建蓝图,名称用于 url_for() 反向解析,__name__ 用于定位模板/静态文件
|
||
user_bp = Blueprint('user', __name__)
|
||
|
||
|
||
@user_bp.route('', methods=['POST'])
|
||
def create_user():
|
||
"""
|
||
创建新用户
|
||
- POST /api/users
|
||
- Body: {"username": "string", "email": "string"}
|
||
- Returns: 201 + 用户JSON / 400 + 错误信息 / 500 + 服务器错误
|
||
"""
|
||
# silent=True 使得非 JSON 请求体返回 None 而非抛出 400 异常
|
||
data = request.get_json(silent=True)
|
||
|
||
# ✅ 入参校验:永远不要信任客户端输入
|
||
if not data or 'username' not in data or 'email' not in data:
|
||
return jsonify({'error': 'Missing required fields: username, email'}), 400
|
||
|
||
try:
|
||
user = User(username=data['username'], email=data['email'])
|
||
db.session.add(user)
|
||
# commit 同时完成 flush(发送SQL)和事务提交
|
||
db.session.commit()
|
||
return jsonify(user.to_dict()), 201
|
||
except Exception as e:
|
||
# ✅ 异常时必须回滚,否则后续请求复用该 session 会持续报错
|
||
db.session.rollback()
|
||
# 生产环境应记录日志而非直接返回异常详情,此处为模板演示
|
||
return jsonify({'error': str(e)}), 500
|
||
|
||
|
||
@user_bp.route('', methods=['GET'])
|
||
def get_users():
|
||
"""
|
||
分页获取用户列表
|
||
- GET /api/users?page=1&per_page=20
|
||
- Returns: 分页结果 JSON,包含 items、total、pages、current_page
|
||
"""
|
||
# type=int 确保参数被转换为整数,非法值回退到默认值
|
||
page = request.args.get('page', 1, type=int)
|
||
per_page = request.args.get('per_page', 20, type=int)
|
||
|
||
# paginate() 自动处理 LIMIT/OFFSET,error_out=False 避免页码越界时抛异常
|
||
pagination = User.query.order_by(User.created_at.desc()).paginate(
|
||
page=page, per_page=per_page, error_out=False
|
||
)
|
||
|
||
return jsonify({
|
||
'items': [u.to_dict() for u in pagination.items],
|
||
'total': pagination.total, # 总记录数
|
||
'pages': pagination.pages, # 总页数
|
||
'current_page': page, # 当前页码
|
||
}) |