Repository navigation
Expand file tree
/
Copy pathapi_errors.py
More file actions
67 lines (55 loc) · 2.48 KB
/
Copy pathapi_errors.py
File metadata and controls
67 lines (55 loc) · 2.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
"""Consistent public errors without leaking request data or internal exceptions."""
import logging
from http import HTTPStatus
from fastapi import FastAPI, Request
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from starlette.exceptions import HTTPException
from product_service import ProductPersistenceError, ProductValidationError
logger = logging.getLogger(__name__)
def error_response(status: int, detail: object, code: str) -> JSONResponse:
return JSONResponse(
status_code=status,
content={"detail": detail, "error": {"code": code}},
)
def register_error_handlers(app: FastAPI) -> None:
@app.exception_handler(HTTPException)
async def http_error(_request: Request, exc: HTTPException) -> JSONResponse:
try:
code = HTTPStatus(exc.status_code).name.lower()
except ValueError:
code = "http_error"
if exc.status_code == 422:
code = "validation_error"
response = error_response(exc.status_code, exc.detail, code)
if exc.headers:
response.headers.update(exc.headers)
return response
@app.exception_handler(RequestValidationError)
async def invalid_request(
_request: Request, exc: RequestValidationError
) -> JSONResponse:
# Keep FastAPI's detail list but omit input and ctx (potential secrets,
# non-JSON values, and exception objects).
details = [
{"loc": error["loc"], "msg": error["msg"], "type": error["type"]}
for error in exc.errors()
]
return error_response(422, details, "validation_error")
@app.exception_handler(ProductValidationError)
async def invalid_product(
_request: Request, exc: ProductValidationError
) -> JSONResponse:
return error_response(422, str(exc), "validation_error")
@app.exception_handler(ProductPersistenceError)
async def persistence_failure(
_request: Request, _exc: ProductPersistenceError
) -> JSONResponse:
logger.error("Product persistence operation failed")
return error_response(
500, "Erro interno ao acessar os produtos", "internal_server_error"
)
@app.exception_handler(Exception)
async def unexpected_failure(_request: Request, _exc: Exception) -> JSONResponse:
logger.error("Unhandled application error (%s)", type(_exc).__name__)
return error_response(500, "Erro interno do servidor", "internal_server_error")