REST API

使用 Flask 构建返回 JSON 的 REST 风格 API。Flask 本身就足够轻量,配合蓝图做版本化管理即可,无需额外框架。

基本结构

视图直接返回 dict/list 时,Flask 会自动序列化为 JSON 并设置 Content-Type: application/json

from flask import Blueprint, request

api = Blueprint("api", __name__, url_prefix="/api/v1")

@api.get("/users")
def list_users():
    return [{"id": 1, "name": "Alice"}]          # 列表直接返回(Flask 2.2+)

@api.get("/users/<int:user_id>")
def get_user(user_id):
    user = find_user(user_id)
    if user is None:
        return {"error": "Not Found"}, 404
    return user

@api.post("/users")
def create_user():
    data = request.get_json()
    if not data or "name" not in data:
        return {"error": "name is required"}, 400
    user = save_user(data)
    return user, 201

@api.delete("/users/<int:user_id>")
def delete_user(user_id):
    remove_user(user_id)
    return "", 204

请求体校验

request.get_json() 在请求不是 JSON 时会抛 415/400,可用 silent=True 改为返回 None 自行处理。生产项目建议用 Pydantic 或 Marshmallow 做结构化校验:

from pydantic import BaseModel, ValidationError

class UserIn(BaseModel):
    name: str
    email: str
    age: int | None = None

@api.post("/users")
def create_user():
    try:
        payload = UserIn.model_validate(request.get_json(silent=True) or {})
    except ValidationError as e:
        return {"error": "validation failed", "detail": e.errors()}, 422
    ...

分页

约定查询参数并返回统一结构:

@api.get("/posts")
def list_posts():
    page = request.args.get("page", 1, type=int)
    per_page = request.args.get("per_page", 20, type=int)
    pagination = Post.query.order_by(Post.id.desc()).paginate(
        page=page, per_page=min(per_page, 100), error_out=False
    )
    return {
        "items": [p.to_dict() for p in pagination.items],
        "total": pagination.total,
        "page": page,
        "per_page": pagination.per_page,
    }

统一错误响应

让 API 蓝图下的错误也返回 JSON(而不是 HTML 错误页):

from werkzeug.exceptions import HTTPException

@api.errorhandler(HTTPException)
def api_http_error(e):
    return {"error": e.name, "message": e.description}, e.code

认证

常见方案:

  • Session Cookie:同域前后端一体应用最简单。
  • Token(Bearer):客户端在 Authorization: Bearer <token> 头中携带;服务端校验后放入 g.current_user
  • JWT:使用 Flask-JWT-Extended 扩展,支持刷新令牌、过期管理。
from functools import wraps
from flask import g, request

def auth_required(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        token = (request.headers.get("Authorization") or "").removeprefix("Bearer ").strip()
        user = verify_token(token)
        if user is None:
            return {"error": "Unauthorized"}, 401
        g.current_user = user
        return f(*args, **kwargs)
    return wrapper

跨域(CORS)

前后端分离部署在不同域名时需要启用 CORS:

pip install flask-cors
from flask_cors import CORS

CORS(api, origins=["https://app.example.com"])   # 只对 API 蓝图开放,并限制来源

版本化建议

  • URL 前缀版本化(/api/v1/)最直观,每个版本一个蓝图。
  • 破坏性变更(删字段、改语义)才升版本;新增字段保持向后兼容即可。