When initilizing FastAPI, an exception_handlers dictionary can be passed, mapping Exceptions/Error Codes to callables. When using this method to set the exception handlers, the HTTPException and RequestValidationError can not be overwritten.
import logging
import uvicorn
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError
from fastapi.exception_handlers import request_validation_exception_handler
logger = logging.getLogger(__name__)
# app = FastAPI()
# @app.exception_handler(RequestValidationError)
async def my_request_validation_exception_handler(
req: Request, exc: RequestValidationError
):
logger.info("my_request_validation_exception_handler")
return await request_validation_exception_handler(req, exc)
app = FastAPI(exception_handlers={RequestValidationError: my_request_validation_exception_handler})
@app.get("/test")
def test(param: int):
return JSONResponse(
status_code=200,
content={"message": "successful test with param {}".format(param)},
)
if __name__ == "__main__":
uvicorn.run("api:app", host="localhost", port=2000, reload=True)
Now a curl "http://localhost:2000/test" will not show the logger.info("my_request_validation_exception_handler") log statement. However, when using the commented out code it works fine. I want to use the other way to keep the handlers in a separate file.
As far as i can see this is coming from the setup method
self.add_exception_handler(HTTPException, http_exception_handler)
self.add_exception_handler(
RequestValidationError, request_validation_exception_handler
)
Starlette will overwrite the custom handlers in the add_exception_handler method
def add_exception_handler(
self,
exc_class_or_status_code: typing.Union[int, typing.Type[Exception]],
handler: typing.Callable,
) -> None:
self.exception_handlers[exc_class_or_status_code] = handler
self.middleware_stack = self.build_middleware_stack()
Would be great if setup() could first check whether a handler for this exception exists already.
It looks like FastAPI 0.60.1 keeps acting the same (wrong?) way,
It works here... But it didn't show anything with the logger above (I've stolen the uvicorn), I've created a minimal example:
import logging
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse
logger = logging.getLogger("uvicorn")
class CustomException(HTTPException):
pass
async def custom_exception_handler(
req: Request, exc: RequestValidationError
):
logger.info("IT WORKS!!!")
return JSONResponse(status_code=exc.status_code)
app = FastAPI(exception_handlers={CustomException: custom_exception_handler})
@app.get("/test")
def test(param: int):
raise CustomException(status_code=400)
Edit: about the logger, it needs a handler, so it's normal that it didn't work...
from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse
from fastapi.exception_handlers import request_validation_exception_handler
from fastapi.exceptions import RequestValidationError
from starlette.exceptions import HTTPException as StarletteHTTPException
async def validation_exception_handler(request, exception):
print("validation_exception_handler WORKS")
return await request_validation_exception_handler(request, exception)
async def http_exception_handler(request, exception):
print("http_exception_handler WORKS")
return JSONResponse(status_code=exception.status_code)
async def starlette_http_exception_handler(request, exception):
print("starlette_http_exception_handler WORKS")
return JSONResponse(status_code=exception.status_code)
app = FastAPI(exception_handlers={
StarletteHTTPException: starlette_http_exception_handler,
HTTPException: http_exception_handler,
RequestValidationError: validation_exception_handler
})
@app.get("/test")
def test(param: int):
raise HTTPException(status_code=400, detail="OOPS")
@Kludex It looks like only HTTPException call the handler..
Ah, I see what you mean, confirmed here as well. Anyone wants to fix it? :tada:
What is the reason for not defining these handlers on instantiation?
Hi @Kludex
I fix this issue in #1924 PR.
Is it the same as this? https://github.com/tiangolo/fastapi/pull/1887/
Looks like there are two issues related to the same bug.
I looked through opened PR but didn't find #1887 PR.
Basically #1924 and #1887 a little bit different, what I should do with my PR?
Well, in the past when @tiangolo saw one first, he basically merged that one (if it was good) and the next was discharged. So I recommend you to mention #1887 on your solution and explain that you've implemented it without knowing that it was already implemented. Then you just wait. :sunglasses: :+1:
Any tips on where this will be solved (merged in this case)?
Same issue with me, any update if this is solved/merged?
@michaeltoohig @kozhushman You can inherit from FastAPI and override setup method.
If update original code provided @timbmg it will look like this:
import logging
import uvicorn
from fastapi import FastAPI as BaseFastAPI, Request
from fastapi.exception_handlers import request_validation_exception_handler, http_exception_handler
from fastapi.exceptions import RequestValidationError, HTTPException
from fastapi.responses import JSONResponse
class FastAPI(BaseFastAPI):
def setup(self) -> None:
_original_add_exception_handler = self.add_exception_handler
def _add_exception_handler(*_):
pass
self.add_exception_handler = _add_exception_handler
super().setup()
if HTTPException not in self.exception_handlers:
self.add_exception_handler(HTTPException, http_exception_handler)
if RequestValidationError not in self.exception_handlers:
self.add_exception_handler(RequestValidationError, request_validation_exception_handler)
logger = logging.getLogger(__name__)
async def my_request_validation_exception_handler(req: Request, exc: RequestValidationError):
logger.info("my_request_validation_exception_handler")
return await request_validation_exception_handler(req, exc)
app = FastAPI(exception_handlers={RequestValidationError: my_request_validation_exception_handler})
@app.get("/test")
def test(param: int):
return JSONResponse(
status_code=200,
content={"message": "successful test with param {}".format(param)},
)
if __name__ == "__main__":
uvicorn.run("api:app", host="localhost", port=2000, reload=True)
Thanks for all the discussion here everyone! :coffee:
@uriyyo fixed this in https://github.com/tiangolo/fastapi/pull/1924, available in FastAPI version 0.61.2. :tada:
Most helpful comment
Thanks for all the discussion here everyone! :coffee:
@uriyyo fixed this in https://github.com/tiangolo/fastapi/pull/1924, available in FastAPI version
0.61.2. :tada: