|
|
@@ -3,12 +3,77 @@ __init__.py -
|
|
|
auther: tanlie
|
|
|
date: 2026/8/30
|
|
|
"""
|
|
|
-from fastapi import FastAPI
|
|
|
+import logging
|
|
|
+import traceback
|
|
|
+from fastapi import FastAPI, Request, HTTPException
|
|
|
+from fastapi.exceptions import RequestValidationError
|
|
|
+from fastapi.responses import JSONResponse
|
|
|
|
|
|
from app.mobile import register_mobile_routes
|
|
|
+from app.core.response import error_response
|
|
|
+
|
|
|
+# Basic logging configuration for the app; applications may override this
|
|
|
+logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s")
|
|
|
+logger = logging.getLogger("app")
|
|
|
|
|
|
|
|
|
def create_app():
|
|
|
app = FastAPI()
|
|
|
register_mobile_routes(app)
|
|
|
+
|
|
|
+ @app.exception_handler(RequestValidationError)
|
|
|
+ async def validation_exception_handler(request: Request, exc: RequestValidationError):
|
|
|
+ # Build a friendly, JSON-serializable summary of validation errors
|
|
|
+ errors = []
|
|
|
+ for e in exc.errors():
|
|
|
+ loc = e.get("loc", [])
|
|
|
+ # remove leading location indicators like 'body', 'query', 'path', 'header'
|
|
|
+ if loc and len(loc) > 0 and str(loc[0]) in ("body", "query", "path", "header"):
|
|
|
+ loc_parts = loc[1:]
|
|
|
+ else:
|
|
|
+ loc_parts = loc
|
|
|
+ # join remaining location parts into a dotted path for readability
|
|
|
+ loc_str = ".".join([str(p) for p in loc_parts if p is not None]) if loc_parts else ""
|
|
|
+ errors.append({"field": loc_str, "message": e.get("msg", "")})
|
|
|
+
|
|
|
+ # Log validation details for debugging
|
|
|
+ logger.info("Validation error for %s: %s", request.url.path, errors)
|
|
|
+
|
|
|
+ return JSONResponse(
|
|
|
+ status_code=422,
|
|
|
+ content=error_response(msg="参数校验失败", code=422, data=errors),
|
|
|
+ )
|
|
|
+
|
|
|
+ @app.exception_handler(HTTPException)
|
|
|
+ async def http_exception_handler(request: Request, exc: HTTPException):
|
|
|
+ # Use HTTPException's status_code and detail
|
|
|
+ detail = exc.detail if exc.detail is not None else ""
|
|
|
+ logger.warning("HTTPException for %s: %s", request.url.path, detail)
|
|
|
+ return JSONResponse(
|
|
|
+ status_code=getattr(exc, "status_code", 500),
|
|
|
+ content=error_response(msg=str(detail), code=getattr(exc, "status_code", 500)),
|
|
|
+ )
|
|
|
+
|
|
|
+ @app.exception_handler(Exception)
|
|
|
+ async def generic_exception_handler(request: Request, exc: Exception):
|
|
|
+ # Map common ValueError to a friendlier 400 parameter error
|
|
|
+ if isinstance(exc, ValueError):
|
|
|
+ # Log with stack for debugging
|
|
|
+ tb = "".join(traceback.format_exception(type(exc), exc, exc.__traceback__))
|
|
|
+ logger.warning("ValueError on %s: %s\n%s", request.url.path, str(exc), tb)
|
|
|
+ return JSONResponse(
|
|
|
+ status_code=400,
|
|
|
+ content=error_response(msg=str(exc) or "参数错误", code=400),
|
|
|
+ )
|
|
|
+
|
|
|
+ # Generic fallback to avoid leaking internal stack traces
|
|
|
+ # Log full stack trace for internal debugging
|
|
|
+ tb = "".join(traceback.format_exception(type(exc), exc, exc.__traceback__))
|
|
|
+ logger.error("Unhandled exception on %s: %s\n%s", request.url.path, str(exc), tb)
|
|
|
+
|
|
|
+ return JSONResponse(
|
|
|
+ status_code=500,
|
|
|
+ content=error_response(msg="服务器内部错误", code=500),
|
|
|
+ )
|
|
|
+
|
|
|
return app
|