How do I integrate custom exception handling with the FastAPI exception handling?

Question:

Python version 3.9, FastAPI version 0.78.0

I have a custom function that I use for application exception handling. When requests run into internal logic problems, i.e I want to send an HTTP response of 400 for some reason, I call a utility function.

@staticmethod
def raise_error(error: str, code: int) -> None:
    logger.error(error)
    raise HTTPException(status_code=code, detail=error)

Not a fan of this approach. So I look at

from fastapi import FastAPI, HTTPException, status
from fastapi.respones import JSONResponse

class ExceptionCustom(HTTPException):
    pass


def exception_404_handler(request: Request, exc: HTTPException):
    return JSONResponse(status_code=status.HTTP_404_NOT_FOUND, content={"message": "404"})


app.add_exception_handler(ExceptionCustom, exception_404_handler)

The problem I run into with the above approach is the inability to pass in the message as an argument.

Any thoughts on the whole topic?

Asked By: reza

||

Answers:

Your custom exception can have any custom attributes that you want. Let’s say you write it this way:

class ExceptionCustom(HTTPException):
    pass 

in your custom handler, you can do something like

def exception_404_handler(request: Request, exc: HTTPException):
    return JSONResponse(status_code=status.HTTP_404_NOT_FOUND, content={"message": exc.detail})

Then, all you need to do is to raise the exception this way:

raise ExceptionCustom(status_code=404, detail='error message')

Note that you are creating a handler for this specific ExceptionCustom. If all you need is the message, you can write something more generic:

class MyHTTPException(HTTPException):
    pass
def my_http_exception_handler(request: Request, exc: HTTPException):
    return JSONResponse(status_code=exc.status_code, content={"message": exc.detail})
app.add_exception_handler(MyHTTPException, my_http_exception_handler)

This way you can raise any exception, with any status code and any message and have the message in your JSON response.

There’s a detailed explanation on FastAPI docs

Answered By: Thiago Salvatore

You can add custom exception handlers, and use attributes in your Exception class (i.e., class MyException(Exception) in the example below) to pass any message/variables that you would like to do so. The exception handler (i.e., @app.exception_handler(MyException) in the case below) will handle the exception as you wish and return your custom message. For more options, please have a look at this related answer as well.

Working Example

To trigger the exception in the example below, access the following URL from your browser: http://localhost:8000/something

from fastapi import FastAPI, Request, status
from fastapi.responses import JSONResponse

class MyException(Exception):
    def __init__(self, name: str):
        self.name = name

app = FastAPI()

@app.exception_handler(MyException)
async def my_exception_handler(request: Request, exc: MyException):
    return JSONResponse(status_code=status.HTTP_404_NOT_FOUND, 
        content={"message": f"{exc.name} cannot be found." })

@app.get("/{name}")
def read_name(name: str):
    if name == "something":
        raise MyException(name=name)
    return {"name": name}

In case you wouldn’t like to use the @app.exception_handler() decorator, you could remove the decorator from the my_exception_handler() funciton and instead use the add_exception_handler() method to add. Example:

app.add_exception_handler(MyException, my_exception_handler)

Another way to add the exception handler to the app would be to use the exception_handlers parameter of the FastAPI class, as demonstrated here. Related answers can also be found here and here.

Answered By: Chris