__init__.py 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. """
  2. __init__.py -
  3. auther: tanlie
  4. date: 2026/8/30
  5. """
  6. import logging
  7. import traceback
  8. from fastapi import FastAPI, Request, HTTPException
  9. from fastapi.exceptions import RequestValidationError
  10. from fastapi.responses import JSONResponse
  11. from app.mobile import register_mobile_routes
  12. from app.core.response import error_response
  13. # Basic logging configuration for the app; applications may override this
  14. logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s")
  15. logger = logging.getLogger("app")
  16. def create_app():
  17. app = FastAPI()
  18. register_mobile_routes(app)
  19. @app.exception_handler(RequestValidationError)
  20. async def validation_exception_handler(request: Request, exc: RequestValidationError):
  21. # Build a friendly, JSON-serializable summary of validation errors
  22. errors = []
  23. for e in exc.errors():
  24. loc = e.get("loc", [])
  25. # remove leading location indicators like 'body', 'query', 'path', 'header'
  26. if loc and len(loc) > 0 and str(loc[0]) in ("body", "query", "path", "header"):
  27. loc_parts = loc[1:]
  28. else:
  29. loc_parts = loc
  30. # join remaining location parts into a dotted path for readability
  31. loc_str = ".".join([str(p) for p in loc_parts if p is not None]) if loc_parts else ""
  32. errors.append({"field": loc_str, "message": e.get("msg", "")})
  33. # Log validation details for debugging
  34. logger.info("Validation error for %s: %s", request.url.path, errors)
  35. return JSONResponse(
  36. status_code=422,
  37. content=error_response(msg="参数校验失败", code=422, data=errors),
  38. )
  39. @app.exception_handler(HTTPException)
  40. async def http_exception_handler(request: Request, exc: HTTPException):
  41. # Use HTTPException's status_code and detail
  42. detail = exc.detail if exc.detail is not None else ""
  43. logger.warning("HTTPException for %s: %s", request.url.path, detail)
  44. return JSONResponse(
  45. status_code=getattr(exc, "status_code", 500),
  46. content=error_response(msg=str(detail), code=getattr(exc, "status_code", 500)),
  47. )
  48. @app.exception_handler(Exception)
  49. async def generic_exception_handler(request: Request, exc: Exception):
  50. # Map common ValueError to a friendlier 400 parameter error
  51. if isinstance(exc, ValueError):
  52. # Log with stack for debugging
  53. tb = "".join(traceback.format_exception(type(exc), exc, exc.__traceback__))
  54. logger.warning("ValueError on %s: %s\n%s", request.url.path, str(exc), tb)
  55. return JSONResponse(
  56. status_code=400,
  57. content=error_response(msg=str(exc) or "参数错误", code=400),
  58. )
  59. # Generic fallback to avoid leaking internal stack traces
  60. # Log full stack trace for internal debugging
  61. tb = "".join(traceback.format_exception(type(exc), exc, exc.__traceback__))
  62. logger.error("Unhandled exception on %s: %s\n%s", request.url.path, str(exc), tb)
  63. return JSONResponse(
  64. status_code=500,
  65. content=error_response(msg="服务器内部错误", code=500),
  66. )
  67. return app