14 KiB
Обробка Помилок
Є багато ситуацій, коли потрібно повідомити клієнта, який використовує ваш API, про помилку.
Цим клієнтом може бути браузер із фронтендом, код іншого розробника, IoT-пристрій тощо.
Можливо, Вам потрібно повідомити клієнта, що:
- У нього недостатньо прав для виконання цієї операції.
- Він не має доступу до цього ресурсу.
- Елемент, до якого він намагається отримати доступ, не існує.
- тощо.
У таких випадках зазвичай повертається HTTP статус-код в діапазоні 400 (від 400 до 499).
Це схоже на HTTP статус-коди 200 (від 200 до 299). Ці "200" статус-коди означають, що запит пройшов успішно.
Статус-коди в діапазоні 400 означають, що сталася помилка з боку клієнта.
Пам'ятаєте всі ці помилки 404 Not Found (і жарти про них)?
Використання HTTPException
Щоб повернути HTTP-відповіді з помилками клієнту, використовуйте HTTPException
.
Імпорт HTTPException
{* ../../docs_src/handling_errors/tutorial001.py hl[1] *}
Використання HTTPException
у коді
HTTPException
— це звичайна помилка Python із додатковими даними, які стосуються API.
Оскільки це помилка Python, Ви не повертаєте
його, а генеруєте
(генеруєте помилку).
Це також означає, що якщо Ви перебуваєте всередині допоміжної функції, яку викликаєте всередині своєї функції операції шляху, і там генеруєте HTTPException
, всередині цієї допоміжної функції, то решта коду в функції операції шляху не буде виконана. Запит одразу завершиться, і HTTP-помилка з HTTPException
буде надіслана клієнту.
Перевага використання генерації
(raise) помилки замість повернення
значення (return) стане більш очевидним в розділі про Залежності та Безпеку.
У цьому прикладі, якщо клієнт запитує елемент за ID, якого не існує, буде згенеровано помилку зі статус-кодом 404
:
{* ../../docs_src/handling_errors/tutorial001.py hl[11] *}
Отримана відповідь
Якщо клієнт робить запит за шляхом http://example.com/items/foo
(де item_id
"foo"
), він отримає статус-код 200 і JSON відповідь:
{
"item": "The Foo Wrestlers"
}
Але якщо клієнт робить запит на http://example.com/items/bar
(де item_id
має не існуюче значення "bar"
), то отримає статус-код 404 (помилка "не знайдено") та відповідь:
{
"detail": "Item not found"
}
/// tip | Порада
Під час виклику HTTPException
Ви можете передати будь-яке значення, яке може бути перетворене в JSON, як параметр detail
, а не лише рядок (str
).
Ви можете передати dict
, list
тощо.
Вони обробляються автоматично за допомогою FastAPI та перетворюються в JSON.
///
Додавання власних заголовків
Іноді потрібно додати власні заголовки до HTTP-помилки, наприклад, для певних типів безпеки.
Ймовірно, Вам не доведеться використовувати це безпосередньо у своєму коді.
Але якщо Вам знадобиться це для складного сценарію, Ви можете додати власні заголовки:
{* ../../docs_src/handling_errors/tutorial002.py hl[14] *}
Встановлення власних обробників помилок
Ви можете додати власні обробники помилок за допомогою тих самих утиліт обробки помилок зі Starlette.
Припустимо, у Вас є власний обʼєкт помилки UnicornException
, яке Ви (або бібліотека, яку Ви використовуєте) може згенерувати
(raise
).
І Ви хочете обробляти це виключення глобально за допомогою FastAPI.
Ви можете додати власний обробник виключень за допомогою @app.exception_handler()
:
{* ../../docs_src/handling_errors/tutorial003.py hl[5:7,13:18,24] *}
Тут, якщо Ви звернетеся до /unicorns/yolo
, то згенерується помилка UnicornException
.
Але вона буде оброблена функцією-обробником unicorn_exception_handler
.
Отже, Ви отримаєте зрозумілу помилку зі HTTP-статусом 418
і JSON-відповіддю:
{"message": "Oops! yolo did something. There goes a rainbow..."}
/// note | Технічні деталі
Ви також можете використовувати from starlette.requests import Request
і from starlette.responses import JSONResponse
.
FastAPI надає ті самі starlette.responses
, що й fastapi.responses
, просто для зручності розробника. Але більшість доступних відповідей надходять безпосередньо зі Starlette. Те ж саме стосується і Request
.
///
Перевизначення обробників помилок за замовчуванням
FastAPI має кілька обробників помилок за замовчуванням.
Ці обробники відповідають за повернення стандартних JSON-відповідей, коли Ви генеруєте
(raise
) HTTPException
, а також коли запит містить некоректні дані.
Ви можете перевизначити ці обробники, створивши власні.
Перевизначення помилок валідації запиту
Коли запит містить некоректні дані, FastAPI генерує RequestValidationError
.
І також включає обробник помилок за замовчуванням для нього.
Щоб перевизначити його, імпортуйте RequestValidationError
і використовуйте його з @app.exception_handler(RequestValidationError)
для декорування обробника помилок.
Обробник помилок отримує Request
і саму помилку.
{* ../../docs_src/handling_errors/tutorial004.py hl[2,14:16] *}
Тепер, якщо Ви перейдете за посиланням /items/foo
, замість того, щоб отримати стандартну JSON-помилку:
{
"detail": [
{
"loc": [
"path",
"item_id"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
Ви отримаєте текстову версію:
1 validation error
path -> item_id
value is not a valid integer (type=type_error.integer)
RequestValidationError
проти ValidationError
/// warning | Увага
Це технічні деталі, які Ви можете пропустити, якщо вони зараз не важливі для Вас.
///
RequestValidationError
є підкласом Pydantic ValidationError
.
FastAPI використовує його для того, якщо Ви використовуєте модель Pydantic у response_model
і у ваших даних є помилка, Ви побачили помилку у своєму журналі.
Але клієнт/користувач не побачить її. Натомість клієнт отримає "Internal Server Error" зі статусом HTTP 500
.
Так має бути, якщо у Вас виникла ValidationError
Pydantic у відповіді або деінде у вашому коді (не у запиті клієнта), це насправді є помилкою у Вашому коді.
І поки Ви її виправляєте, клієнти/користувачі не повинні мати доступу до внутрішньої інформації про помилку, оскільки це може призвести до вразливості безпеки.
Перевизначення обробника помилок HTTPException
Аналогічно, Ви можете перевизначити обробник HTTPException
.
Наприклад, Ви можете захотіти повернути текстову відповідь замість JSON для цих помилок:
{* ../../docs_src/handling_errors/tutorial004.py hl[3:4,9:11,22] *}
/// note | Технічні деталі
Ви також можете використовувати from starlette.responses import PlainTextResponse
.
FastAPI надає ті самі starlette.responses
, що й fastapi.responses
, просто для зручності розробника. Але більшість доступних відповідей надходять безпосередньо зі Starlette.
///
Використання тіла RequestValidationError
RequestValidationError
містить body
, який він отримав із некоректними даними.
Ви можете використовувати це під час розробки свого додатка, щоб логувати тіло запиту та налагоджувати його, повертати користувачеві тощо.
{* ../../docs_src/handling_errors/tutorial005.py hl[14] *}
Тепер спробуйте надіслати некоректний елемент, наприклад:
{
"title": "towel",
"size": "XL"
}
Ви отримаєте відповідь, яка повідомить Вам, які саме дані є некоректні у вашому тілі запиту:
{
"detail": [
{
"loc": [
"body",
"size"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
],
"body": {
"title": "towel",
"size": "XL"
}
}
HTTPException
FastAPI проти HTTPException
Starlette
FastAPI має власний HTTPException
.
І клас помилки HTTPException
в FastAPI успадковується від класу помилки HTTPException
в Starlette.
Єдина різниця полягає в тому, що HTTPException
в FastAPI приймає будь-які дані, які можна перетворити на JSON, для поля detail
, тоді як HTTPException
у Starlette приймає тільки рядки.
Отже, Ви можете продовжувати використовувати HTTPException
в FastAPI як зазвичай у своєму коді.
Але коли Ви реєструєте обробник виключень, слід реєструвати його для HTTPException
зі Starlette.
Таким чином, якщо будь-яка частина внутрішнього коду Starlette або розширення чи плагін Starlette згенерує (raise) HTTPException
, Ваш обробник зможе перехопити та обробити її.
У цьому прикладі, щоб мати можливість використовувати обидва HTTPException
в одному коді, помилка Starlette перейменовується на StarletteHTTPException
:
from starlette.exceptions import HTTPException as StarletteHTTPException
Повторне використання обробників помилок FastAPI
Якщо Ви хочете використовувати помилки разом із такими ж обробниками помилок за замовчуванням, як у FastAPI, Ви можете імпортувати та повторно використовувати їх із fastapi.exception_handlers
:
{* ../../docs_src/handling_errors/tutorial006.py hl[2:5,15,21] *}
У цьому прикладі Ви просто використовуєте print
для виведення дуже інформативного повідомлення, але Ви зрозуміли основну ідею. Ви можете обробити помилку та повторно використовувати обробники помилок за замовчуванням.