Files
backend/backend/app/core/error_handlers.py
T
34047007@qq.com a6cd99a4ca
CI / backend (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
feat: initial commit - oncology literature search platform
OncoLit: a multi-tenant oncology literature search, feed, and
collaboration platform. Built with FastAPI + Vue 3 + PostgreSQL.
Includes PubMed pipeline, drug approvals, AI summaries, and
systematic review tools.
2026-07-27 07:59:18 +08:00

54 lines
2.2 KiB
Python

"""全局错误处理 + CSP 安全头中间件"""
from fastapi import HTTPException, Request
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware
from app.config import settings
from app.core.i18n import detect_language, translate
from app.core.logging_config import get_logger
logger = get_logger("scilit.error")
async def global_exception_handler(request: Request, exc: Exception):
"""捕获所有未处理异常,记录日志后返回统一 JSON"""
logger.exception(f"Unhandled exception: {request.method} {request.url.path}")
# Sentry 上报
try:
import sentry_sdk
sentry_sdk.capture_exception(exc)
except ImportError:
pass
lang = detect_language(request.headers.get("accept-language"))
return JSONResponse(
status_code=500,
content={"success": False, "error": {"code": "INTERNAL_ERROR", "message": translate("An unexpected error occurred", lang)}},
)
async def http_exception_handler(request: Request, exc: HTTPException):
"""统一 HTTPException 返回格式,根据请求语言翻译 detail"""
lang = detect_language(request.headers.get("accept-language"))
return JSONResponse(
status_code=exc.status_code,
content={"success": False, "error": {"code": f"HTTP_{exc.status_code}", "message": translate(exc.detail, lang)}},
)
class SecurityHeadersMiddleware(BaseHTTPMiddleware):
"""注入安全头:CSP, X-Frame-Options 等"""
async def dispatch(self, request: Request, call_next):
response = await call_next(request)
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "DENY"
response.headers["X-XSS-Protection"] = "1; mode=block"
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
response.headers["Permissions-Policy"] = "camera=(), microphone=(), geolocation=()"
# 生产环境启用 HSTS(强制 HTTPS,预载 + 子域名)
if not settings.DEBUG:
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains; preload"
# CSP 统一在 nginx 层设置
return response