Исключения FastAPI

В этом посте я хочу поговорить об исключениях в FastAPI. Ну, не только об исключениях, но и о схемах Pydantic и валидации данных запроса. Мне нравится держать прозу на низком уровне, так что давайте сразу же погрузимся!

Рассмотрим это минимальное приложение FastAPI с одной конечной точкой, которая принимает запросы POST с полезной нагрузкой, которая по какой-то причине требует проверки:

from fastapi import FastAPI 
app = FastAPI()
@app.post("/ceate_item"):
def create_item(data: MySchema):
   ...

Когда приходит POST-запрос, FastAPI использует MySchema для проверки данных, поскольку мы объявили, что данные имеют тип MySchema. Предположим, что данные содержат следующее содержимое:

example_data = {
   "name": "john", 
   "surname": "doe", 
   "email": "[email protected]"
}

Схема Pydantic MySchema должна отражать структуру этих данных или, скорее, наоборот: входящие данные должны иметь ту же структуру, что и схема Pydantic. Объявим это:

from pydantic import BaseModel
class MySchema(BaseModel):
   name: str
   surname: str
   email: str

Поле данных, которое потенциально нуждается в проверке, — это поле электронной почты. (Есть готовые валидаторы электронной почты, так что вам не нужно писать свои собственные, но ради этого примера мы не хотим писать валидатор для этого поля.) Вы можете сделать это, импортировав специальный декоратор из пидантический:

from pydantic import BaseModel, validator
class MySchema(BaseModel):
   name: str
   surname: str
   email: str
   @validator("email")
   def ensure_valid_email_(cls, e):
      # let's assume you have written some function
      # 'is_valid' that does the actual validation
      if e not is_valid:
         raise ValueError("email address invalid !")
      return e

Это должно ввести проверку поля электронной почты отправленных данных и вызвать ошибку надлежащего типа. Но эта настройка все еще может быть несколько неудовлетворительной, поскольку сама по себе она не мешает FastAPI передавать HTTPException по умолчанию в ответ, а именно что-то вроде 422 Unprocessable Entitiy. Давайте быстро спустимся в кроличью нору и оставим ее после очень короткого пребывания: Pydantic преобразует наши ошибки ValueError и другие в ValidationError. И, согласно документам FastAPI, всякий раз, когда запрос содержит данные, не прошедшие проверку, FastAPI выдает свою собственную специальную ошибку, которая наследуется от ValidationError Pydantic: RequestValidationError. Название довольно наводящее на размышления и объясняет само себя. Следовательно, все неудачные проверки сводятся к одной и той же ошибке 422 Unprocessable Entitiy. Аааааа из кроличьей норы мы снова !

Чтобы обойти этот несовершенный сценарий, мы должны перезаписать FastAPIs RequestValidationError:

from fastapi import Request
from fastapi.responses import JSONResponse # for example
@app.exception_handler(RequestValidationError)
async def value_error_exception_handler(
   request: Request, exc: ValueError):
   return JSONResponse(
        status_code=400,
        content={"message": str(exc)}
    )

Ну вот. Таким образом, мы можем перехватить нашу ошибку ValueError из схемы Pydantic и преобразовать ее в осмысленную пользовательскую ошибку HTTP.

Спасибо за прочтение и, пожалуйста, поставьте аплодисменты, если вам понравилось! "="