Fast api response error

Есть много ситуаций, когда вам нужно уведомить об ошибке клиента, использующего ваш API.

Есть много ситуаций, когда вам нужно уведомить об ошибке клиента, использующего ваш 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's user avatar

VisioN

142k32 gold badges278 silver badges278 bronze badges

asked Oct 23, 2020 at 13:45

Mojimi's user avatar

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

enter image description here

answered Oct 23, 2020 at 19:09

Yagiz Degirmenci's user avatar

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

Show hidden 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 и Python

Для упрощения в 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.

An example of the error notification system we are going to build.

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

Demonstration of how an exception is sent to Honeybadger for error monitoring.

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.

How additional context is shown inside the web UI.

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 Logo

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.

FastAPI swagger 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.

Animation that shows how the middleware system lets us catch exceptions.

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!

Example of an error notification.

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_includeThe 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_excludeThe 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.
  1. 200 is the default status code, which means everything is «normal.»
  2. Another example would be 201, «Created.» It is typically used after a new record has been created in the database.
  3. 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.
  1. An example is 404 for a «not found» response.
  2. 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

Понравилась статья? Поделить с друзьями:

Читайте также:

  • Farming simulator 22 ошибка 0xc000007b
  • Farming simulator 22 error data files corrupt please reinstall application
  • Farming simulator 2017 game exe системная ошибка
  • Farm together platform initialization error
  • Farcry3 exe ошибка при запуске приложения 0xc000007b

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии