Prompts-MCP 686 skills · ai / design / design-pattern / framework / fundamentals / habit / lang / tech-selection /mcp/sse
framework framework/fastapi/middleware/anonymous-session.md medium

fastapi-anonymous-session

匿名会话中间件 — anon_xxx + 双积分池。Use when 写 Python 后端代码 / 评审涉及 `anonymous-session` 的 PR。

anonymoussessionanon_middleware匿名会话名会话中会话中间
paths
  • backend/middleware/anonymous_session.py
  • **/middleware/anonymous_session.py

FastAPI Middleware · 匿名 Session

规则

适用于匿名 + 邀请的产品模式。每个请求必须带 X-Session-Idanon_<uuid> 格式),中间件自动加载 session 上下文。

中间件实现

# backend/middleware/anonymous_session.py
from fastapi import FastAPI, Request, HTTPException
from starlette.middleware.base import BaseHTTPMiddleware
import logging

logger = logging.getLogger(__name__)

EXEMPT_PATHS = {"/health", "/docs", "/openapi.json", "/redoc"}

class AnonymousSessionMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        if any(request.url.path.startswith(p) for p in EXEMPT_PATHS):
            return await call_next(request)

        session_id = request.headers.get("X-Session-Id")
        if not session_id or not session_id.startswith("anon_"):
            return JSONResponse(
                status_code=401,
                content={"success": False, "msg": "missing or invalid X-Session-Id", "code": 401},
            )

        # 加载 / 创建 session
        session = await SessionRepo.get_or_create(session_id)
        if session.is_blocked:
            return JSONResponse(
                status_code=403,
                content={"success": False, "msg": "session blocked", "code": 403},
            )

        # 注入到 request.state,供后续依赖读取
        request.state.session = session

        response = await call_next(request)
        return response

注册

# backend/main.py
app.add_middleware(AnonymousSessionMiddleware)

依赖读取

# core/deps.py
from fastapi import Request

async def get_current_session(request: Request):
    if not hasattr(request.state, "session"):
        raise HTTPException(status_code=401, detail="no session")
    return request.state.session

双积分池约定

中间件层只负责加载 session,不扣减积分——扣减逻辑在 Service 层。

class Session(Model):
    session_id = fields.CharField(max_length=64, pk=True)  # anon_<uuid>
    base_credits = fields.IntField(default=10)
    bonus_credits = fields.IntField(default=0)
    base_credits_lifetime = fields.IntField(default=10)
    bonus_credits_lifetime = fields.IntField(default=0)
    invite_code = fields.CharField(max_length=16, null=True)
    invited_by_code = fields.CharField(max_length=16, null=True)
    is_blocked = fields.BooleanField(default=False)
    risk_score = fields.IntField(default=0)
    ...

与反爬虫协同

AntiCrawlerMiddlewareAnonymousSessionMiddleware 之前运行——先评分再加载。

自检

  • [ ] 中间件加载顺序:AntiCrawler → AnonymousSession → Auth → Logging?
  • [ ] 排除路径不需要 session(/health / /docs)?
  • [ ] is_blocked session 直接 403?
  • [ ] session 挂在 request.state.session?

相关