feat(system):优化后端
1.新增后端测试 2.增加了后端的加密 3.增加了i18n(国际化)
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
"""``api/__init__.py`` 全局异常兜底 handler 与 CORS 中间件。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import JSONResponse
|
||||
from httpx import AsyncClient, ASGITransport
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app_with_global_handler():
|
||||
"""构造一个最小 app,套用与生产相同的兜底 handler。"""
|
||||
from kilostar.utils.logger import get_logger
|
||||
|
||||
_logger = get_logger("api")
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@app.exception_handler(Exception)
|
||||
async def unhandled_exception_handler(request: Request, exc: Exception):
|
||||
_logger.exception(
|
||||
f"Unhandled exception on {request.method} {request.url.path}: {exc}"
|
||||
)
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
content={"message": "服务内部错误,请稍后重试"},
|
||||
)
|
||||
|
||||
@app.get("/boom")
|
||||
async def boom():
|
||||
raise RuntimeError("internal stack trace with secrets")
|
||||
|
||||
return app
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_unhandled_exception_returns_masked_500(app_with_global_handler):
|
||||
transport = ASGITransport(app=app_with_global_handler, raise_app_exceptions=False)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
||||
resp = await client.get("/boom")
|
||||
|
||||
assert resp.status_code == 500
|
||||
body = resp.json()
|
||||
assert body == {"message": "服务内部错误,请稍后重试"}
|
||||
# 关键:不能把内部 traceback 透出
|
||||
assert "internal stack trace with secrets" not in resp.text
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cors_preflight_with_explicit_origin():
|
||||
app = FastAPI()
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["https://app.example.com"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
@app.get("/x")
|
||||
async def _x():
|
||||
return {"ok": True}
|
||||
|
||||
transport = ASGITransport(app=app)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
||||
resp = await client.options(
|
||||
"/x",
|
||||
headers={
|
||||
"Origin": "https://app.example.com",
|
||||
"Access-Control-Request-Method": "GET",
|
||||
},
|
||||
)
|
||||
|
||||
assert resp.headers.get("access-control-allow-origin") == "https://app.example.com"
|
||||
assert resp.headers.get("access-control-allow-credentials") == "true"
|
||||
Reference in New Issue
Block a user