add excpetion_handlers module

This commit is contained in:
grillazz 2025-08-24 09:49:04 +02:00
parent 71d90aa1e3
commit ee637b53e0
6 changed files with 111 additions and 0 deletions

View File

@ -0,0 +1,3 @@
from app.exception_handlers.registry import register_exception_handlers
__all__ = ["register_exception_handlers"]

View File

@ -0,0 +1,34 @@
# app/exception_handlers/base.py
import orjson
from fastapi import Request
from fastapi.responses import JSONResponse
from rotoger import AppStructLogger
logger = AppStructLogger().get_logger()
class BaseExceptionHandler:
"""Base class for all exception handlers with common functionality."""
@staticmethod
async def extract_request_info(request: Request):
"""Extract common request information."""
request_path = request.url.path
try:
raw_body = await request.body()
request_body = orjson.loads(raw_body) if raw_body else None
except orjson.JSONDecodeError:
request_body = None
return request_path, request_body
@classmethod
async def log_error(cls, message, request_info, **kwargs):
"""Log error with standardized format."""
request_path, request_body = request_info
await logger.aerror(
message,
request_url=request_path,
request_body=request_body,
**kwargs
)

View File

@ -0,0 +1,24 @@
from fastapi import Request
from fastapi.responses import JSONResponse
from sqlalchemy.exc import SQLAlchemyError
from app.exception_handlers.base import BaseExceptionHandler
class SQLAlchemyExceptionHandler(BaseExceptionHandler):
"""Handles SQLAlchemy database exceptions."""
@classmethod
async def handle_exception(cls, request: Request, exc: SQLAlchemyError) -> JSONResponse:
request_info = await cls.extract_request_info(request)
await cls.log_error(
"Database error occurred",
request_info,
sql_error=repr(exc)
)
return JSONResponse(
status_code=500,
content={"message": "A database error occurred. Please try again later."}
)

View File

@ -0,0 +1,11 @@
from fastapi import FastAPI
from sqlalchemy.exc import SQLAlchemyError
from fastapi.exceptions import ResponseValidationError
from app.exception_handlers.database import SQLAlchemyExceptionHandler
from app.exception_handlers.validation import ResponseValidationExceptionHandler
def register_exception_handlers(app: FastAPI) -> None:
"""Register all exception handlers with the FastAPI app."""
app.add_exception_handler(SQLAlchemyError, SQLAlchemyExceptionHandler.handle_exception)
app.add_exception_handler(ResponseValidationError, ResponseValidationExceptionHandler.handle_exception)

View File

@ -0,0 +1,39 @@
from fastapi import Request
from fastapi.exceptions import ResponseValidationError
from fastapi.responses import JSONResponse
from app.exception_handlers.base import BaseExceptionHandler
class ResponseValidationExceptionHandler(BaseExceptionHandler):
"""Handles response validation exceptions."""
@classmethod
async def handle_exception(cls, request: Request, exc: ResponseValidationError) -> JSONResponse:
request_info = await cls.extract_request_info(request)
errors = exc.errors()
# Check if this is a None/null response case
is_none_response = False
for error in errors:
if error.get("input") is None and "valid dictionary" in error.get("msg", ""):
is_none_response = True
break
await cls.log_error(
"Response validation error occurred",
request_info,
validation_errors=errors,
is_none_response=is_none_response
)
if is_none_response:
return JSONResponse(
status_code=404,
content={"no_response": "The requested resource was not found"}
)
else:
return JSONResponse(
status_code=422,
content={"response_format_error": errors}
)