mirror of
https://github.com/grillazz/fastapi-sqlalchemy-asyncpg.git
synced 2025-08-26 16:40:40 +03:00
add excpetion_handlers module
This commit is contained in:
parent
71d90aa1e3
commit
ee637b53e0
3
app/exception_handlers/__init__.py
Normal file
3
app/exception_handlers/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
from app.exception_handlers.registry import register_exception_handlers
|
||||
|
||||
__all__ = ["register_exception_handlers"]
|
34
app/exception_handlers/base.py
Normal file
34
app/exception_handlers/base.py
Normal 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
|
||||
)
|
24
app/exception_handlers/database.py
Normal file
24
app/exception_handlers/database.py
Normal 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."}
|
||||
)
|
11
app/exception_handlers/registry.py
Normal file
11
app/exception_handlers/registry.py
Normal 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)
|
39
app/exception_handlers/validation.py
Normal file
39
app/exception_handlers/validation.py
Normal 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}
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user