How do I integrate custom exception handling with the FastAPI exception handling?
Asked Answered
C

2

5

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?

Cubiform answered 1/7, 2022 at 16:5 Comment(0)
I
9

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

Idolah answered 1/7, 2022 at 18:22 Comment(0)
S
6

Option 1

You could add custom exception handlers, and use attributes in your Exception class (i.e., MyException(Exception) in the example below), in order to pass a custom message or variables. The exception handler (in the example below, that is, my_exception_handler() with the @app.exception_handler(MyException) decorator) 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

In order to trigger the exception in the example below, call the /items/{item_id} endpoint using an item_id that is not present in the items dictionary.

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


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


app = FastAPI()

items = {"foo": "The Foo Wrestlers"}


@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"Item for '{exc.item_id}' cannot be found." })


@app.get("/items/{item_id}")
async def read_item(item_id: str):
    if item_id not in items:
        raise MyException(item_id=item_id)
    return {"item": items[item_id]}

In case you wouldn't like using 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 the handler to the app instance. Example:

app.add_exception_handler(MyException, my_exception_handler)

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

Option 2

You could always use HTTPException to return HTTP responses with custom errors to the client (as well as add custom headers to the HTTP error).

Working Example

from fastapi import FastAPI, HTTPException

app = FastAPI()

items = {"foo": "The Foo Wrestlers"}


@app.get("/items/{item_id}")
async def read_item(item_id: str):
    if item_id not in items:
        raise HTTPException(status_code=404, detail="Item not found")
    return {"item": items[item_id]}
Synthesize answered 1/7, 2022 at 18:23 Comment(1)
How does this work ? I am also seeing this but not sure what is the flow of execution here.Hangover

© 2022 - 2024 — McMap. All rights reserved.