|
1 | 1 | import logging
|
2 | 2 |
|
3 | 3 | from fastapi import FastAPI, status
|
4 |
| -from fastapi.encoders import jsonable_encoder |
5 | 4 | from fastapi.requests import Request
|
6 | 5 | from fastapi.responses import JSONResponse
|
7 |
| - |
8 |
| -from sqlalchemy.exc import NoResultFound |
9 |
| - |
| 6 | +from sqlalchemy.exc import IntegrityError, NoResultFound |
10 | 7 |
|
11 | 8 | logger = logging.getLogger(__name__)
|
12 | 9 |
|
13 | 10 |
|
14 | 11 | def register_exception_handlers(app: FastAPI):
|
15 |
| - app.add_exception_handler(NoResultFound, not_found_error_handler) |
16 |
| - app.add_exception_handler(Exception, exception_handler) |
| 12 | + app.add_exception_handler(NoResultFound, handle_no_result_found) |
| 13 | + app.add_exception_handler(IntegrityError, handle_integrity_error) |
| 14 | + app.add_exception_handler(ValueError, handle_value_error) |
| 15 | + app.add_exception_handler(PermissionError, handle_permission_error) |
| 16 | + app.add_exception_handler(ConnectionError, handle_connection_error) |
| 17 | + app.add_exception_handler(Exception, handle_generic_exception) |
17 | 18 |
|
18 | 19 |
|
19 |
| -def not_found_error_handler(request: Request, exc: NoResultFound): |
| 20 | +def handle_no_result_found(request: Request, exc: NoResultFound): |
20 | 21 | return JSONResponse(
|
21 | 22 | status_code=status.HTTP_404_NOT_FOUND,
|
22 |
| - content=jsonable_encoder({"detail": str(exc)}), |
| 23 | + content={"message": "Resource not found", "detail": str(exc)}, |
| 24 | + ) |
| 25 | + |
| 26 | + |
| 27 | +def handle_integrity_error(request: Request, exc: IntegrityError): |
| 28 | + return JSONResponse( |
| 29 | + status_code=status.HTTP_409_CONFLICT, |
| 30 | + content={"message": "Resource already exists", "detail": str(exc)}, |
| 31 | + ) |
| 32 | + |
| 33 | + |
| 34 | +def handle_value_error(request: Request, exc: ValueError): |
| 35 | + logger.exception("Unhandled ValueError: %s", exc) |
| 36 | + return JSONResponse( |
| 37 | + status_code=status.HTTP_400_BAD_REQUEST, |
| 38 | + content={"message": "Invalid input", "detail": str(exc)}, |
| 39 | + ) |
| 40 | + |
| 41 | + |
| 42 | +def handle_permission_error(request: Request, exc: PermissionError): |
| 43 | + logger.exception("Unhandled PermissionError: %s", exc) |
| 44 | + return JSONResponse( |
| 45 | + status_code=status.HTTP_403_FORBIDDEN, |
| 46 | + content={"message": "Permission denied", "detail": str(exc)}, |
| 47 | + ) |
| 48 | + |
| 49 | + |
| 50 | +def handle_connection_error(request: Request, exc: ConnectionError): |
| 51 | + logger.exception("Unhandled ConnectionError: %s", exc) |
| 52 | + return JSONResponse( |
| 53 | + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, |
| 54 | + content={"message": "Service unavailable", "detail": str(exc)}, |
23 | 55 | )
|
24 | 56 |
|
25 | 57 |
|
26 |
| -def exception_handler(request: Request, exc: Exception): |
27 |
| - """Global exception handler.""" |
28 |
| - logger.exception("Unhandled error during request %s", request) |
| 58 | +def handle_generic_exception(request: Request, exc: Exception): |
| 59 | + logger.exception("Unhandled exception: %s", exc) |
29 | 60 | return JSONResponse(
|
30 | 61 | status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
31 |
| - content={"detail": "server error"}, |
| 62 | + # Do not expose error details |
| 63 | + content={"message": "Server error"}, |
32 | 64 | )
|
0 commit comments