Перейти к содержанию

HTTP-ответ

Раздел описывает как можно сформировать и отправить HTTP-ответ в Rapidy.

Описание

HTTP-ответ — это сообщение, которое сервер отправляет клиенту в ответ на его запрос.

Пример текстового HTTP-ответа (протокол HTTP/1.1)
HTTP/1.1 200 OK
Server: Nginx
Content-Type: text/html; charset=utf-8
Date: Wed, 10 Aug 2024 11:00:00 GMT
Keep-Alive: timeout=5, max=1000
Connection: Keep-Alive
Age: 3464
Date: Wed, 10 Aug 2024 11:10:00 GMT
X-Cache-Info: caching
Content-Length: 220

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN>">
(... more body data ...)

Структура HTTP-ответа

HTTP-ответ состоит из стартовой строки, заголовков и тела.

Стартовая строка

HTTP/1.1 200 OK

Стартовая строка (или строка статуса) включает:

  • Версию протокола (HTTP-протокол)HTTP/1.1
  • Код состояния (числовой код, обозначающий статус запроса)200
  • Пояснение (краткое текстовое описание кода состояния) — OK
Версии HTTP-протокола

HTTP-стандарты разрабатываются Internet Engineering Task Force (IETF) и World Wide Web Consortium (W3C), что приводит к публикации серии документов Requests for Comments (RFC).

Версия протокола Тип HTTP-протокола Транспортный уровень Описание
HTTP/1.1 Текстовый TCP Требует дожидаться ответа перед отправкой следующего запроса в рамках одного соединения.
HTTP/2 Бинарный TCP Позволяет отправлять несколько запросов одновременно без ожидания завершения предыдущих.
HTTP/3/QUIC Бинарный UDP Работает поверх UDP (использует технологию QUIC).
Коды состояния HTTP

HTTP-коды состояния сообщают клиенту результат обработки его запроса. Они делятся на пять категорий:

Код Описание
1xx Информационные коды, не влияющие на обработку запроса.
2xx Успешная обработка запроса.
3xx Перенаправление клиента на другой ресурс.
4xx Ошибки на стороне клиента (например, неверный запрос или недостаток прав доступа).
5xx Ошибки на стороне сервера.

Заголовки ответа

Заголовки ответа (Response Headers) уточняют детали ответа, и никак не влияют на содержимое тела.

Примеры заголовков
Категория Пример Описание
Server Server: nginx Информация о сервере, обработавшем запрос.
Set-Cookie Set-Cookie:UserData=SomeData123 Cookie с информацией о пользователе, сохраняемой браузером.

Тело ответа

Опциональная часть ответа, содержащая данные.

Сервер указывает тип передаваемых данных с помощью заголовка Content-Type.

Тело ответа может представлять собой JSON, медиафайл, документ, текст или даже произвольный набор байтов.

Формирование HTTP-ответа

Простой ответ http-обработчика может выглядеть так:

from rapidy.http import get

@get('/')
async def handler() -> dict[str, str]:
    return {'hello': 'rapidy'}

Валидация и сериализация ответа

Для валидации и сериализации ответов Rapidy использует pydantic.

При запуске сервера Rapidy создает pydantic-модель для каждого обработчика на основе аннотации возврата и использует её для валидации данных в ответе.

Можно переопределить тип для валидации или отменить создание pydantic-модели с помощью атрибутов response_validate и response_type.

from rapidy.http import get

@get(
    '/',
    response_validate=False,
)
async def handler() -> str:  # <-- `str` will be ignored
    return {'hello': 'rapidy'}

Подробнее об аттрибутах ответа для http-обработчика можно прочитать здесь.

Примеры успешных ответов

@post('/')
async def handler() -> int:  # <-- `int` will be used to validate
    return '123'  # success response --> `123`
from pydantic import BaseModel, Field

class Result(BaseModel):
    data: str = Field(max_length=10)

@post('/')
async def handler() -> Result:
    return Result(data='some_data')  # success response --> `{"data": "some_data"}`
from pydantic import BaseModel, Field

class Result(BaseModel):
    data: str = Field(max_length=10)

@post('/')
async def handler() -> Result:
    return {'data': 'some_data'}  # # success response --> `{"data": "some_data"}`

Примеры неуспешных ответов

from rapidy.http import get

@get('/')
async def handler() -> int:  # <-- `int` will be used to validate
    return 'some_data'  # <-- will raise err
------------------------------
Validation errors:
 [{'loc': ('body',),
  'msg': 'Input should be a valid integer, unable to parse string as an '
         'integer',
  'type': 'int_parsing'}]

Handler path: `main.py`
Handler name: `handler`
------------------------------

from pydantic import BaseModel, Field

class Result(BaseModel):
    data: str = Field(max_length=10)

@post('/')
async def handler() -> Result:
    return {'data': 'another_data'}  # <-- will raise err
------------------------------
Validation errors:
 [{'ctx': {'max_length': 10},
  'loc': ('body', 'data'),
  'msg': 'String should have at most 10 characters',
  'type': 'string_too_long'}]

Handler path: `main.py`
Handler name: `handler`
------------------------------

Продвинутый уровень управления HTTP-ответом

Для управления HTTP-ответами Rapidy использует объект Response.

from rapidy.http import Response

Объект Response может быть создан как самим Rapidy внутри себя для формирования ответа, так и разработчиком для явного управления им.

Подробнее об объекте Response можно прочитать здесь.

Автоматическое создание Response-объекта

Rapidy автоматически создает Response в следующих случаях:

В обработчике определен атрибут с любым именем и типом Response

from rapidy.http import get, Response

@get('/')
async def handler(response: Response) -> ...:
    ...

Это даёт разработчику больше гибкости в управлении HTTP-ответами, позволяя, например, устанавливать статус-код, cookies и другие параметры. Подробнее о параметрах объекта Response можно узнать здесь.

from rapidy.http import get, Response

@get('/')
async def handler(
    response: Response,  # <-- current response
) -> str:
    some_answer: bool = ...
    if some_answer:
        response.set_status(200)
        return 'ok'

    response.set_status(500)
    return 'not ok'

Также можно вернуть этот же Response объект.

from rapidy.http import get, Response

@get('/')
async def handler(response: Response) -> Response:
    return response

Обработчик возвращает python объект

from rapidy.http import get

@get('/')
async def handler() -> dict[str, str]:
    return {'hello': 'rapidy'}

Если в обработчике уже определён атрибут с типом Response, а сам обработчик возвращает python-объект, то новый экземпляр Response создаваться не будет.

from rapidy.http import get, Response

@get('/')
async def handler(
    response: Response,  # <-- this response will be returned
) -> str:
    return 'ok'

Обработчик возвращает Response-объект

Rapidy позволяет разработчику самостоятельно управлять и формировать Response объект.

from rapidy.http import get, Response

@get('/')
async def handler() -> Response:
    return Response(status=201)

При прямом управлении ответом атрибуты веб-обработчика игнорируются

from rapidy.http import get, ContentType, Response

@get(
    '/',
    response_content_type=ContentType.json,  # <-- will be ignored
)
async def handler() -> Response:
    return Response(...)

Если Response объект был проброшел в аттрибут, а разработчик отдает новый Response то проброшенный Response игнорируется.

from rapidy.http import get, Response

@get('/')
async def handler(
    response: Response,  # <-- this response will be ignored
) -> Response:
    response.set_status(200)  # <-- `200` status will be ignored

    return Response(status=500)  # <-- new Response obj returned status `500`

При прямом управлении ответом валидация pydantic не будет работать.

from rapidy.http import get, Response

@get('/')
async def handler(
) -> int:  # <-- `int` type to validate will be ignored
    return Response()

Обработчик возвращает None

Если обработчик Rapidy ничего не возвращает, то по умолчанию Rapidy вернет текущий Response объект.

Если вы изменили запрос и ничего не вернули из обработчика, то будет отдан именно этот измененный запрос!

from rapidy.http import get, Response

@get('/')
async def handler(response: Response) -> None:
    response.text = 'hello rapidy!'

# success response --> `hello rapidy!`

Атрибуты http-обработчиков

Атрибуты ответа веб-обработчика используются для управления формированием ответа при возврате любого python-объекта из обработчика.

Код состояния по умолчанию

Для управления кодом состояния по умолчанию вы можете определить атрибут status_code.

from http import HTTPStatus
from rapidy.http import get

@get(
    '/',
    status_code=201,
)
async def handler() -> ...:
    ...

@get(
    '/',
    status_code=HTTPStatus.CREATED,
)
async def handler() -> ...:
    ...

Об остальных аттрибутах можно прочитать здесь.

Rapidy позволяет управлять атрибутами во всех типах обработчиков, включая те, что оформлены в стиле aiohttp.

# aiohttp style
from rapidy import web

async def handler() -> dict[str, str]:
    return {'hello': 'rapidy'}

app = web.Application()
app.add_routes([
    web.post(
        '/',
        handler,
        status_code=200,
        response_content_type='application/json',
    ),
])