Есть много ситуаций, когда вам нужно уведомить об ошибке клиента, использующего ваш API.
Этот клиент может быть браузером с внешним интерфейсом, чужим кодом, IoT-устройством и т. д.
Возможно, вам придется сообщить клиенту, что:
- У клиента недостаточно прав для этой операции.
- У клиента нет доступа к этому ресурсу.
- Элемент, к которому пытался получить доступ клиент, не существует.
- etc.
В этих случаях вы обычно возвращаете код состояния HTTP в диапазоне 400 (от 400 до 499).
Это похоже на 200 кодов состояния HTTP (от 200 до 299). Эти коды состояния «200» означают, что запрос каким-то образом был «успешен».
Коды состояния в диапазоне 400 означают, что произошла ошибка клиента.
Помните все эти ошибки «404 Not Found» (и шутки)?
Use HTTPException
Чтобы вернуть клиенту HTTP-ответы с ошибками, вы используете HTTPException
.
Import HTTPException
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]}
Поднимите HTTPException
в своем коде
HTTPException
— это обычное исключение Python с дополнительными данными, относящимися к API.
Поскольку это исключение Python, вы его не return
raise
.
Это также означает, что если вы находитесь внутри служебной функции, которую вы вызываете внутри своей функции операции пути , и вы вызываете HTTPException
изнутри этой служебной функции, она не будет запускать остальную часть кода в функции операции пути , он немедленно завершит этот запрос и отправит клиенту ошибку HTTP из исключения HTTPException
.
Преимущество возбуждения исключения по сравнению с return
значения будет более очевидным в разделе, посвященном зависимостям и безопасности.
В этом примере, когда клиент запрашивает элемент по несуществующему идентификатору, создайте исключение с кодом состояния 404
:
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]}
Полученная реакция
Если клиент запрашивает http://example.com/items/foo
( item_id
"foo"
), этот клиент получит код состояния HTTP 200 и ответ JSON:
{ "item": "The Foo Wrestlers" }
Но если клиент запрашивает http://example.com/items/bar
(несуществующий item_id
"bar"
), этот клиент получит код состояния HTTP 404 (ошибка «не найден») и ответ JSON. из:
{ "detail": "Item not found" }
Tip
При возникновении HTTPException
вы можете передать любое значение, которое может быть преобразовано в JSON, в качестве detail
параметра , а не только str
.
Вы можете передать dict
, list
и т.д.
Они автоматически обрабатываются FastAPI и конвертируются в JSON.
В некоторых ситуациях полезно иметь возможность добавлять собственные заголовки к ошибке HTTP. Например, для некоторых видов безопасности.
Вам, вероятно, не нужно будет использовать его непосредственно в коде.
Но если вам это нужно для расширенного сценария, вы можете добавить собственные заголовки:
from fastapi import FastAPI, HTTPException app = FastAPI() items = {"foo": "The Foo Wrestlers"} @app.get("/items-header/{item_id}") async def read_item_header(item_id: str): if item_id not in items: raise HTTPException( status_code=404, detail="Item not found", headers={"X-Error": "There goes my error"}, ) return {"item": items[item_id]}
Установите пользовательские обработчики исключений
Вы можете добавить собственные обработчики исключений теми же утилитами исключений от Starlette .
Допустим, у вас есть пользовательское исключение UnicornException
, которое вы (или используемая вами библиотека) можете raise
.
И вы хотите обрабатывать это исключение глобально с помощью FastAPI.
Вы можете добавить собственный обработчик исключений с помощью @app.exception_handler()
:
from fastapi import FastAPI, Request from fastapi.responses import JSONResponse class UnicornException(Exception): def __init__(self, name: str): self.name = name app = FastAPI() @app.exception_handler(UnicornException) async def unicorn_exception_handler(request: Request, exc: UnicornException): return JSONResponse( status_code=418, content={"message": f"Oops! {exc.name} did something. There goes a rainbow..."}, ) @app.get("/unicorns/{name}") async def read_unicorn(name: str): if name == "yolo": raise UnicornException(name=name) return {"unicorn_name": name}
UnicornException
вы запросите /unicorns/yolo
, операция пути вызовет raise
UnicornException .
Но это будет обрабатываться unicorn_exception_handler
.
Итак, вы получите чистую ошибку с кодом состояния HTTP 418
и содержимым JSON:
{"message": "Oops! yolo did something. There goes a rainbow..."}
Technical Details
Вы также можете использовать from starlette.requests import Request
и from starlette.responses import JSONResponse
.
FastAPI предоставляет те же starlette.responses
, что и fastapi.responses
, только для удобства вас, разработчика. Но большинство доступных ответов исходит непосредственно от Старлетт. То же самое с Request
.
Переопределить обработчики исключений по умолчанию
FastAPI имеет несколько обработчиков исключений по умолчанию.
Эти обработчики отвечают за возврат ответов JSON по умолчанию, когда вы HTTPException
raise
когда запрос содержит недопустимые данные.
Вы можете переопределить эти обработчики исключений своими собственными.
Переопределить исключения проверки запроса
Когда запрос содержит недопустимые данные, FastAPI внутренне вызывает RequestValidationError
.
И он также включает в себя обработчик исключений по умолчанию.
Чтобы переопределить его, импортируйте RequestValidationError
и используйте его с @app.exception_handler(RequestValidationError)
для оформления обработчика исключений.
Обработчик исключений получит Request
и исключение.
from fastapi import FastAPI, HTTPException from fastapi.exceptions import RequestValidationError from fastapi.responses import PlainTextResponse from starlette.exceptions import HTTPException as StarletteHTTPException app = FastAPI() @app.exception_handler(StarletteHTTPException) async def http_exception_handler(request, exc): return PlainTextResponse(str(exc.detail), status_code=exc.status_code) @app.exception_handler(RequestValidationError) async def validation_exception_handler(request, exc): return PlainTextResponse(str(exc), status_code=400) @app.get("/items/{item_id}") async def read_item(item_id: int): if item_id == 3: raise HTTPException(status_code=418, detail="Nope! I don't like 3.") return {"item_id": item_id}
Теперь, если вы перейдете к /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
— это подкласс ValidationError
Pydantic .
FastAPI использует его, поэтому, если вы используете модель Pydantic в response_model
и в ваших данных есть ошибка, вы увидите ошибку в своем журнале.
Но клиент/пользователь этого не увидит. Вместо этого клиент получит «Внутреннюю ошибку сервера» с кодом состояния HTTP 500
.
Так и должно быть, потому что если у вас есть Pydantic ValidationError
в вашем ответе или где-либо в вашем коде (не в запросе клиента ), это на самом деле ошибка в вашем коде.
И пока вы ее исправляете, ваши клиенты/пользователи не должны иметь доступа к внутренней информации об ошибке, так как это может привести к уязвимости системы безопасности.
Переопределить обработчик ошибок HTTPException
Точно так же вы можете переопределить обработчик HTTPException
.
Например, вы можете захотеть вернуть простой текстовый ответ вместо JSON для этих ошибок:
from fastapi import FastAPI, HTTPException from fastapi.exceptions import RequestValidationError from fastapi.responses import PlainTextResponse from starlette.exceptions import HTTPException as StarletteHTTPException app = FastAPI() @app.exception_handler(StarletteHTTPException) async def http_exception_handler(request, exc): return PlainTextResponse(str(exc.detail), status_code=exc.status_code) @app.exception_handler(RequestValidationError) async def validation_exception_handler(request, exc): return PlainTextResponse(str(exc), status_code=400) @app.get("/items/{item_id}") async def read_item(item_id: int): if item_id == 3: raise HTTPException(status_code=418, detail="Nope! I don't like 3.") return {"item_id": item_id}
Technical Details
Вы также можете использовать from starlette.responses import PlainTextResponse
.
FastAPI предоставляет те же starlette.responses
, что и fastapi.responses
, только для удобства вас, разработчика. Но большинство доступных ответов исходит непосредственно от Старлетт.
Используйте тело RequestValidationError
Ошибка RequestValidationError
содержит полученное body
Вы можете использовать его при разработке своего приложения для регистрации тела и его отладки, возврата пользователю и т. д.
from fastapi import FastAPI, Request, status from fastapi.encoders import jsonable_encoder from fastapi.exceptions import RequestValidationError from fastapi.responses import JSONResponse from pydantic import BaseModel app = FastAPI() @app.exception_handler(RequestValidationError) async def validation_exception_handler(request: Request, exc: RequestValidationError): return JSONResponse( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, content=jsonable_encoder({"detail": exc.errors(), "body": exc.body}), ) class Item(BaseModel): title: str size: int @app.post("/items/") async def create_item(item: Item): return item
Теперь попробуйте отправить недопустимый элемент, например:
{ "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 наследуется от класса ошибок HTTPException
Starlette .
Единственное отличие состоит в том, что HTTPException
FastAPI позволяет вам добавлять заголовки, которые будут включены в ответ.
Это необходимо/используется внутри OAuth 2.0 и некоторых утилит безопасности.
Таким образом, вы можете продолжать вызывать HTTPException FastAPI , HTTPException
обычно, в своем коде.
Но когда вы регистрируете обработчик исключений, вы должны зарегистрировать его для HTTPException
Starlette .
Таким образом, если какая-либо часть внутреннего кода Starlette, расширение или подключаемый модуль Starlette вызывает исключение HTTPException
Starlette , ваш обработчик сможет его перехватить и обработать.
В этом примере, чтобы иметь возможность иметь оба HTTPException
в одном и том же коде, исключения Starlette переименовываются в StarletteHTTPException
:
from starlette.exceptions import HTTPException as StarletteHTTPException
Повторно использовать обработчики исключений FastAPI
Если вы хотите использовать исключение вместе с теми же обработчиками исключений по умолчанию из FastAPI , вы можете импортировать и повторно использовать обработчики исключений по умолчанию из fastapi.exception_handlers
:
from fastapi import FastAPI, HTTPException from fastapi.exception_handlers import ( http_exception_handler, request_validation_exception_handler, ) from fastapi.exceptions import RequestValidationError from starlette.exceptions import HTTPException as StarletteHTTPException app = FastAPI() @app.exception_handler(StarletteHTTPException) async def custom_http_exception_handler(request, exc): print(f"OMG! An HTTP error!: {repr(exc)}") return await http_exception_handler(request, exc) @app.exception_handler(RequestValidationError) async def validation_exception_handler(request, exc): print(f"OMG! The client sent invalid data!: {exc}") return await request_validation_exception_handler(request, exc) @app.get("/items/{item_id}") async def read_item(item_id: int): if item_id == 3: raise HTTPException(status_code=418, detail="Nope! I don't like 3.") return {"item_id": item_id}
В этом примере вы просто print
ошибку с очень выразительным сообщением, но вы поняли идею. Вы можете использовать исключение, а затем просто повторно использовать обработчики исключений по умолчанию.
FastAPI
0.86
-
Extra Models
-
First Steps
-
Header Parameters
-
Tutorial — User Guide — Intro
The documentation suggests raising an HTTPException with client errors, which is great.
But how can I show those specific errors in the documentation following HTTPException’s model? Meaning a dict with the «detail» key.
The following does not work because HTTPException is not a Pydantic model.
@app.get(
'/test',
responses={
409 : {
'model' : HTTPException,
'description': 'This endpoint always raises an error'
}
}
)
def raises_error():
raise HTTPException(409, detail='Error raised')
VisioN
142k32 gold badges278 silver badges278 bronze badges
asked Oct 23, 2020 at 13:45
1
Yes it is not a valid Pydantic type however since you can create your own models, it is easy to create a Model for it.
from fastapi import FastAPI
from fastapi.exceptions import HTTPException
from pydantic import BaseModel
class Dummy(BaseModel):
name: str
class HTTPError(BaseModel):
detail: str
class Config:
schema_extra = {
"example": {"detail": "HTTPException raised."},
}
app = FastAPI()
@app.get(
"/test",
responses={
200: {"model": Dummy},
409: {
"model": HTTPError,
"description": "This endpoint always raises an error",
},
},
)
def raises_error():
raise HTTPException(409, detail="Error raised")
I believe this is what you are expecting
answered Oct 23, 2020 at 19:09
Yagiz DegirmenciYagiz Degirmenci
14.2k4 gold badges53 silver badges80 bronze badges
2
Permalink
Cannot retrieve contributors at this time
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from fastapi.encoders import jsonable_encoder | |
from fastapi.exceptions import RequestValidationError | |
from fastapi.utils import is_body_allowed_for_status_code | |
from starlette.exceptions import HTTPException | |
from starlette.requests import Request | |
from starlette.responses import JSONResponse, Response | |
from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY | |
async def http_exception_handler(request: Request, exc: HTTPException) -> Response: | |
headers = getattr(exc, «headers», None) | |
if not is_body_allowed_for_status_code(exc.status_code): | |
return Response(status_code=exc.status_code, headers=headers) | |
return JSONResponse( | |
{«detail»: exc.detail}, status_code=exc.status_code, headers=headers | |
) | |
async def request_validation_exception_handler( | |
request: Request, exc: RequestValidationError | |
) -> JSONResponse: | |
return JSONResponse( | |
status_code=HTTP_422_UNPROCESSABLE_ENTITY, | |
content={«detail»: jsonable_encoder(exc.errors())}, | |
) |
Отправка статусных кодов
Последнее обновление: 06.09.2022
Одной из расспространненых задач в веб-приложении является отправка статусных кодов, которые указывают на статус выполнения операции на сервере.
-
1xx: предназначены для информации. Ответ с таким кодом не может иметь содержимого
-
2xx: указывает на успешноее выполнение операции
-
3xx: предназначены для переадресации
-
4xx: предназначены для отправки информации об ошибок клиента
-
5xx: предназначены для информации об ошибках сервера
По умолчанию функции обработки отправляют статусный код 200, но при необходимости мы можем отправить любой статусный код. Для этого у методов get()
, post()
, put()
, delete()
, options()
, head()
, patch()
,
trace()
в классе FastAPI применяется параметр status_code
,
который принимает числовой код статуса HTTP. Например:
.
from fastapi import FastAPI app = FastAPI() @app.get("/notfound", status_code=404) def notfound(): return {"message": "Resource Not Found"}
В данном случае при обращении по пути «/notfound» клиенту отправляется статусный код ошибки 404, который говорит о том, что ресурс не найден.
Для упрощения в FastAPI есть модуль status, в котором определены константы для представления статусных кодов:
-
HTTP_100_CONTINUE
(код 100) -
HTTP_101_SWITCHING_PROTOCOLS
(код 101) -
HTTP_102_PROCESSING
(код 102) -
HTTP_103_EARLY_HINTS
(код 103) -
HTTP_200_OK
(код 200) -
HTTP_201_CREATED
(код 201) -
HTTP_202_ACCEPTED
(код 202) -
HTTP_203_NON_AUTHORITATIVE_INFORMATION
(код 203) -
HTTP_204_NO_CONTENT
(код 204) -
HTTP_205_RESET_CONTENT
(код 205) -
HTTP_206_PARTIAL_CONTENT
(код 206) -
HTTP_207_MULTI_STATUS
(код 207) -
HTTP_208_ALREADY_REPORTED
(код 208) -
HTTP_226_IM_USED
(код 226) -
HTTP_300_MULTIPLE_CHOICES
(код 300) -
HTTP_301_MOVED_PERMANENTLY
(код 301) -
HTTP_302_FOUND
(код 302) -
HTTP_303_SEE_OTHER
(код 303) -
HTTP_304_NOT_MODIFIED
(код 304) -
HTTP_305_USE_PROXY
(код 305) -
HTTP_306_RESERVED
(код 306) -
HTTP_307_TEMPORARY_REDIRECT
(код 307) -
HTTP_308_PERMANENT_REDIRECT
(код 308) -
HTTP_400_BAD_REQUEST
(код 400) -
HTTP_401_UNAUTHORIZED
(код 401) -
HTTP_402_PAYMENT_REQUIRED
(код 402) -
HTTP_403_FORBIDDEN
(код 403) -
HTTP_404_NOT_FOUND
(код 404) -
HTTP_405_METHOD_NOT_ALLOWED
(код 405) -
HTTP_406_NOT_ACCEPTABLE
(код 406) -
HTTP_407_PROXY_AUTHENTICATION_REQUIRED
(код 407) -
HTTP_408_REQUEST_TIMEOUT
(код 408) -
HTTP_409_CONFLICT
(код 409) -
HTTP_410_GONE
(код 410) -
HTTP_411_LENGTH_REQUIRED
(код 411) -
HTTP_412_PRECONDITION_FAILED
(код 412) -
HTTP_413_REQUEST_ENTITY_TOO_LARGE
(код 413) -
HTTP_414_REQUEST_URI_TOO_LONG
(код 414) -
HTTP_415_UNSUPPORTED_MEDIA_TYPE
(код 415) -
HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE
(код 416) -
HTTP_417_EXPECTATION_FAILED
(код 417) -
HTTP_418_IM_A_TEAPOT
(код 418) -
HTTP_421_MISDIRECTED_REQUEST
(код 421) -
HTTP_422_UNPROCESSABLE_ENTITY
(код 422) -
HTTP_423_LOCKED
(код 423) -
HTTP_424_FAILED_DEPENDENCY
(код 424) -
HTTP_425_TOO_EARLY
(код 425) -
HTTP_426_UPGRADE_REQUIRED
(код 426) -
HTTP_428_PRECONDITION_REQUIRED
(код 428) -
HTTP_429_TOO_MANY_REQUESTS
(код 429) -
HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE
(код 431) -
HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS
(код 451) -
HTTP_500_INTERNAL_SERVER_ERROR
(код 500) -
HTTP_501_NOT_IMPLEMENTED
(код 501) -
HTTP_502_BAD_GATEWAY
(код 502) -
HTTP_503_SERVICE_UNAVAILABLE
(код 503) -
HTTP_504_GATEWAY_TIMEOUT
(код 504) -
HTTP_505_
-
HTTP_VERSION_NOT_SUPPORTED
(код 505) -
HTTP_506_VARIANT_ALSO_NEGOTIATES
(код 506) -
HTTP_507_INSUFFICIENT_STORAGE
(код 507) -
HTTP_508_LOOP_DETECTED
(код 508) -
HTTP_510_NOT_EXTENDED
(код 510) -
HTTP_511_NETWORK_AUTHENTICATION_REQUIRED
(код 511)
Пример использования
from fastapi import FastAPI app = FastAPI() @app.get("/notfound", status_code=status.HTTP_404_NOT_FOUND) def notfound(): return {"message": "Resource Not Found"}
Определение статусного кода в ответе
В примере выше функция вне зависимости от данных запроса или каких-то других условий в любом случае возвращала статусный код 404. Однако чаще бывает необходимо возвращать статусный код в
зависимости от некоторых условий. В этом случае мы можем использовать параметр status_code конструктора класса Response или его наследников:
from fastapi import FastAPI from fastapi.responses import JSONResponse app = FastAPI() @app.get("/notfound") def notfound(): return JSONResponse(content={"message": "Resource Not Found"}, status_code=404)
Изменение статусного кода
Можно комбинировать оба подхода:
from fastapi import FastAPI, Response, Path app = FastAPI() @app.get("/users/{id}", status_code=200) def users(response: Response, id: int = Path()): if id < 1: response.status_code = 400 return {"message": "Incorrect Data"} return {"message": f"Id = {id}"}
В данном случае если параметр пути меньше 1, то условно считаем, что переданные некорректные данные, и отправляем в ответ статусный код 400 (Bad Request)
НазадСодержаниеВперед
«Errors should never pass silently» is easy to say, but complicated to achieve. A robust application needs a powerful monitoring system to control, debug errors, and promptly alert you when something goes wrong. These are the problems Honeybadger is dedicated to solving.
Honeybadger is a universal platform for error, uptime, and check-in monitoring, combined in a single, powerful application. By including it in your production stack, you will be able to keep track of all the exceptions occurring in your programs, constantly monitor their health status, and know when your background jobs and services go missing or silently fail.
When it comes to error monitoring, a lot of ground is covered by web applications. FastAPI is a micro web framework with increasing popularity in the Python community. It embraces all the modern features added to Python in recent years, such as type hinting and async support, and leverages this power by giving you a lot of flexibility and features, such as automatic and interactive documentation, mixed sync/async support, and built-in type-checking.
In this article, we will explore some of the features that Honeybadger has to offer, in particular its support of the Python language and some of the ways you can use it to monitor your web applications, with a special focus on FastAPI.
Set Up
Like many other tools, Honeybadger comes with support for a wide range of programming languages, including Ruby, JavaScript, Go, and many others. Getting access to the official Python APIs is as easy as installing the official Honeybadger Python package. This can be done with the usual command:
For a more comprehensive overview of all the features offered by this library, you may want to check out the documentation. However, for a basic setup, all we need to do is use the honeybadger.configure
method, along with some configuration. We can do this in literally two lines of code:
from honeybadger import honeybadger
honeybadger.configure(environment="development")
What this is doing for us under the hood is wrapping any existing exception hooks so that a notification can be sent to the Honeybadger dashboard when an error occurs.
Let’s write a «buggy» script to put it into practice:
import logging
from honeybadger import honeybadger
logging.basicConfig(level=logging.INFO) # set up logging.
honeybadger.configure(environment="development")
if __name__ == "__main__":
1 / 0
We’re working in a development
environment here, so we get a log message telling us that the exception is not being sent to the monitoring system.
For a fully working example, we can sign up for a free Honeybadger account and be up and running in less than 30 seconds.
After creating a new project, we’ll be provided with an API token. The honeybadger
library will look for a HONEYBADGER_API_KEY
environment variable holding the value of this token, or we can choose to declare it directly to the honeybadger.configure
call as an alternative.
There are various ways to export environment variables, but if you’re on Linux, you can use the following command:
export HONEYBADGER_API_KEY=SomeApiToken
However, you may also choose to declare the api_key
directly in your code:
from honeybadger import honeybadger
honeybadger.configure(api_key="this-overrides-SomeApiToken")
Head over to the documentation for a full list of environment variables and parameters available for configuring Honeybadger.
Now we’re all set for a fully working example. Run this code to see it in action:
from honeybadger import honeybadger
honeybadger.configure(api_key="<your-api-key>")
1 / 0
Sending notifications explicitly
There are situations in which we will need to send notifications directly. This is the case of a try/except
statement where we may want to handle exceptions but get a notification when they occur. For these cases, we can use a honeybadger.notify
method to explicitly send our notifications.
from honeybadger import honeybadger
honeybadger.configure()
some_dictionary = {"some_key": "some_value"}
try:
some_dictionary["some_other_key"]
except KeyError as err:
honeybadger.notify(err, context={"some_dictionary": some_dictionary})
We make use of the optional context
parameter to pass additional context that may be useful for debugging, replicating, or identifying the exception from the dashboard.
So far, we’ve seen how easy it is to integrate Honeybadger into any Python program. Let’s see how we could use this to set it up in the context of a web application.
FastAPI
FastAPI is a modern, fast web framework for building APIs with Python 3.6+. Some of its best features include speed and serialization/deserialization standards that will give you Swagger interactive documentation out-of-the-box. Moreover, you will be able to mix your sync and async code within the same application.
To set it up, we can install it as follows:
Create a main.py
application file:
###
# main.py
from fastapi import FastAPI
app = FastAPI(title="Honeybadger")
@app.get("/")
async def root():
return {"message": "Hello World"}
And run it as a web server:
pip install uvicorn && uvicorn main:app --reload
Let’s point our browser to http://localhost:8000/docs
to get the full benefits of the interactive documentation.
Middleware
One of the great things about FastAPI is its dependency injection system and the ability to create middleware to enrich your application with custom behavior while handling each request.
In particular, this gives us the opportunity to catch any exceptions raised by our application and notify our monitoring system.
A middleware, in our context, is a simple asynchronous function with two parameters:
- A
request
object, that will include the request data being sent. - A
call_next
callable that will let us call the following step on the request-handling chain.
We will use the @app.middleware
decorator to register our middleware to the application:
@app.middleware("http")
async def honeybadger_middleware(request, call_next):
try:
await call_next(request)
except Exception as err:
honeybadger.notify(err, context={"request": request})
raise err
What we’re doing here is trying to continue with the resolution of the incoming request and notify any errors bubbling up before allowing the exception to continue its way up to FastAPI’s default exception handling system, which is in charge of returning default JSON responses every time an HTTPException
is raised or when the request has invalid data.
All that’s left to do is include a failing route in our application to test our monitoring system:
@app.get("/divide_by_zero", response_model=int)
def divide_by_zero(a: int = 42):
"""Will try to divide `a` by zero."""
return a / 0
The updated main.py
file should look like this:
###
# main.py
from honeybadger import honeybadger
from fastapi import FastAPI
"""
Step 0: configure Honeybadger.
> Tip: configuring it before anything else lets us catch any errors that could be raised *before* the application gets executed.
"""
honeybadger.configure()
app = FastAPI(title="Honeybadger")
"""
Step 1: implement and register the middleware.
"""
@app.middleware("http")
async def honeybadger_middleware(request, call_next):
try:
response = await call_next(request)
except Exception as err:
honeybadger.notify(err, context={"request": request})
raise err
return response
@app.get("/")
async def root():
return {"message": "Hello World"}
"""
Step 2: add some failing code.
"""
@app.get("/divide_by_zero", response_model=int)
def divide_by_zero(a: int = 42):
"""Will try to divide `a` by zero."""
return a / 0
Now let’s head over to our failing route http://localhost:8000/divide_by_zero
(or the Swagger UI), let our code fail, and get a full error report inside our Honeybadger Dashboard!
Wrapping up
We’ve seen how easy it is to integrate Honeybadger into any of your Python applications or FastAPI servers.
We know that it may still look like too much effort, so we’ve thought of including native ASGI support in our library.
What this means is that you can do all the above in just one line of code, for any application supporting the ASGI standards, just like FastAPI, Starlette, Uvicorn, and many more.
This approach lets us replace our middleware implementation with a app.add_middleware
call that will register the contrib.ASGIHoneybadger
plugin instead:
from fastapi import FastAPI
from honeybadger import honeybadger, contrib
honeybadger.configure()
app = FastAPI(title="Honeybadger")
app.add_middleware(contrib.ASGIHoneybadger) # ⚡
@app.get("/")
async def root():
return {"message": "Hello World"}
@app.get("/divide_by_zero", response_model=int)
def divide_by_zero(a: int = 42):
"""Will try to divide `a` by zero."""
return a / 0
You’re done. Pretty straightforward, isn’t it? ✨
Check out the official documentation for any configuration details or advanced usage.
Happy coding!
Just a quick ramble
We talked a little bit about cookies and headers, but today we’re going to talk a little bit about responses
The response is the return value of the interface, the status code, etc., which is required. The data returned is mainly used as a reference for the front-end debug page and testing.
Response model
Fastapi simply uses the response_model parameter in any path (@app.get(), @app.post(), @app.put(), @app.delete()) operation to declare the model used for the response.
Note: response_model is an argument to the «decorator» method (get, POST, etc.). Unlike all the previous arguments and request bodies, it is not part of the path manipulation function.
from typing import List, Optional
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
tax: Optional[float] = None
tagg: List[str] = []
@app.post("/items/", response_model=Item)
async def create_item(item: Item):
return item
Copy the code
Response_model is defined as the return value, because the response_model is assigned by Item and returns the same data as the input
Start the service:
PS E:git_codepython-codefastapiProject uvicorn response_main:app --reload
Copy the code
Request interface:
POST http://127.0.0.1:8000/items
Request parameters:
{"name": "zhang ", "price": 3.2}Copy the code
Request result:
{" name ":" zhang ", "description", null, "price" : 3.2, "tax", null, "tagg" : []}Copy the code
If we enter a password, then the above return the same data as the input is not suitable for this requirement, so we have to define the output model:
from typing import List, Optional
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
from fastapi import status
app = FastAPI()
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: Optional[str] = None
class UserOut(BaseModel):
username: str
email: EmailStr
full_name: Optional[str] = None
@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn):
return user
Copy the code
Note information:
EmailStr is the mail type. UserIn is the input model and UserOut is the output model. Response_model =UserOut is the received output model. User: UserIn is the received input model. Return user Although the input model is returned, we have already declared the output model, so FastAPI will take care of filtering out all data that is not declared in the output model.
Start the service:
PS E:git_codepython-codefastapiProject uvicorn response_main:app --reload
Copy the code
Request interface:
POST http://127.0.0.1:8000/user
Request parameters:
{
"username": "lifeng",
"password":"123456",
"email": "[email protected]",
"full_name": "debuglifeng"
}
Copy the code
Request result:
{
"username": "lifeng",
"email": "[email protected]",
"full_name": "debuglifeng"
}
Copy the code
The response_model_exclude_unset parameter returns only the specified value and defines an ID to handle the specified return value.
Response_model_exclude_unset preach True
from typing import List, Optional
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
from fastapi import status
app = FastAPI()
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
tax: Optional[float] = None
tagg: List[str] = []
@app.post("/items/", response_model=Item, response_model_exclude_unset=True)
async def create_item(item: Item):
return item
Copy the code
Response_model_exclude_unset =True; response_model_exclude_unset=True;
Start the service:
PS E:git_codepython-codefastapiProject uvicorn response_main:app --reload
Copy the code
Request interface:
POST http://127.0.0.1:8000/items
Request parameters:
{"name": "zhang ", "price": 3.2}Copy the code
Request result:
{"name": "zhang ", "price": 3.2}Copy the code
Specify the return value
Send the specified ID to the path operation. Such data is available in the database:
from typing import List, Optional from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class Default(BaseModel): name: STR description: STR = None Price: float Tax: float = 10.5 Tags: List[STR] = [] items = {" foo": {"name": "Foo", "price", 50.2}, "bar" : {" name ":" bar ", "description" : "The bartenders", "price" : 62, "tax", 20.2}, "baz" : {" name ":" Baz ", "description" : None, "price" : 50.2, "tax" : 10.5, "tags" : [] }, } @app.post( "/default/{item_id}", response_model=Default) async def default_items(item_id: str): return items[item_id]Copy the code
Items is a large dictionary that can then fetch data from the database
Start the service:
PS E:git_codepython-codefastapiProject uvicorn response_main:app --reload
Copy the code
Request interface:
POST http://127.0.0.1:8000/default/bar
Request parameters:
{"name": "zhangsan", "price": 10.22}Copy the code
Request result:
{" name ":" Bar ", "description" : "The bartenders", "price" : 62.0, "tax" : 20.2, "tags" : []}Copy the code
Response_model_include and response_model_exclude, response_model_include, response_model_exclude, response_model_exclude, response_model_include, response_model_exclude, response_model_exclude:
response_model_include
The ginseng
from typing import List, Optional from fastapi import FastAPI from pydantic import BaseModel, EmailStr app = FastAPI() class Item(BaseModel): name: str description: Optional[str] = None price: float tax: Optional [float] = 3.33 tagg: List[str] = [] @app.post("/items/", response_model=Item, response_model_include={"name"}) async def create_item(item: Item): return itemCopy the code
Request parameters:
{"name": "zhang ", "price": 3.2}Copy the code
Request result:
{"name": "zhang SAN"}Copy the code
response_model_exclude
The ginseng
from typing import List, Optional from fastapi import FastAPI from pydantic import BaseModel, EmailStr app = FastAPI() class Item(BaseModel): name: str description: Optional[str] = None price: float tax: Optional [float] = 3.33 tagg: List[str] = [] @app.post("/items/", response_model=Item, response_model_exclude={"price"}) async def create_item(item: Item): return itemCopy the code
Request parameters:
{"name": "zhang ", "price": 3.2}Copy the code
Request result:
{" name ":" zhang ", "description", null, "tax" : 3.33, "tagg" : []}Copy the code
Response status code
Fastapi only needs to use the status_code parameter in any path (@app.get(), @app.post(), @app.put(), @app.delete()) operation to declare the HTTP status code for the response.
Pass the status code directly
from fastapi import FastAPI
app = FastAPI()
@app.post("/items/", status_code=201)
async def items(name: str):
return {"name": name}
Copy the code
The status_code parameter receives a number representing the HTTP status code.
The status status package is imported
from fastapi import FastAPI, status
app = FastAPI()
@app.post("/items/", status_code=status.HTTP_201_CREATED)
async def items(name: str):
return {"name": name}
Copy the code
HTTP status code description:
- Status codes 100 and above are used for message responses. You rarely use them directly. A response with these status codes cannot have a response body.
- Status codes 200 and above are used for successful responses. These are the ones you use most often.
- 200 is the default status code, which means everything is «normal.»
- Another example would be 201, «Created.» It is typically used after a new record has been created in the database.
- A special example is 204, «no content.» This response is used when no content is returned to the client, so the response cannot contain the response body.
- Status codes 300 or higher are used for redirection. Responses with these status codes may or may not have a response body, with the exception of 304 «Unmodified», which may not contain a response body.
- Status codes 400 and above are used for client error responses. These are probably your second most commonly used types.
- An example is 404 for a «not found» response.
- For common errors from clients, you can just use 400.
- Status codes 500 and above are used for server-side errors. You almost never use them directly. When something goes wrong in your application code or in some part of the server, it automatically returns one of these status codes.
Today, let’s stop here. The above summary may help you, or may not help you, but I still hope to help you. If there is doubt, ambiguity, direct private message will be timely revised and released; I am looking forward to your three-touch [like, favorites, share] oh, thank you!
Unfinished, to be continued…
Have been trying, I hope you are too!
Wechat search public account: use Python