Преобразовать публичные поля в тело запроса#
Используйте BodyProjection, когда вызывающий передаёт плоские предметные значения, а внешний
API ждёт вложенный служебный документ. Проекция выполняется для каждой попытки и возвращает
структуру, которую затем кодирует выбранное представление тела.
→ Request · POST /register
{
"account": {"login": "ada", "email": "ada@mail.example"},
"client": "python"
}
← Response · 201 Created
{"id": "account-42", "login": "ada"}
# examples/docs/request_projection.py
@dataclass(frozen=True, slots=True, kw_only=True)
class RegisterUser(HttpOperation[Account]):
__http__ = Http.post(
"/register",
success={201: Account},
projection=BodyProjection(
target=RegistrationWire,
using=lambda source: {
"account": {"login": source.login, "email": source.email},
"client": "python",
},
encoding=Body.json(),
name="registration-v1",
),
)
login: str
email: str
class RegistrationApi(SyncApi):
register = op(RegisterUser)
target описывает закрытую структуру на проводе. using получает значение операции и строит
документ. encoding=Body.json() выбирает JSON, а name даёт проекции устойчивое имя для
диагностики и схемы.
Файл примера целиком
"""Project flat public inputs into a nested service-owned JSON document."""
from __future__ import annotations
from dataclasses import dataclass
from typing import TypedDict
from eazy_sdk import BodyProjection, Http, HttpOperation, SyncApi, op
from eazy_sdk.request import Body
from examples.docs.request_site import request_client
class AccountWire(TypedDict):
login: str
email: str
class RegistrationWire(TypedDict):
account: AccountWire
client: str
@dataclass(frozen=True, slots=True)
class Account:
id: str
login: str
# docs:projection:start
# examples/docs/request_projection.py
@dataclass(frozen=True, slots=True, kw_only=True)
class RegisterUser(HttpOperation[Account]):
__http__ = Http.post(
"/register",
success={201: Account},
projection=BodyProjection(
target=RegistrationWire,
using=lambda source: {
"account": {"login": source.login, "email": source.email},
"client": "python",
},
encoding=Body.json(),
name="registration-v1",
),
)
login: str
email: str
class RegistrationApi(SyncApi):
register = op(RegisterUser)
# docs:projection:end
def main() -> None:
with request_client() as client:
account = RegistrationApi(client).register(
login="ada",
email="ada@mail.example",
)
print(f"registered: {account.login} as {account.id}")
if __name__ == "__main__":
main()
Запуск: uv run python examples/docs/request_projection.py.
registered: ada as account-42
Разделить уровни представления#
Уровень |
Что он меняет |
Где настраивается |
|---|---|---|
имя поля |
Python-имя в имя на проводе |
маркер или адаптер модели |
документ |
публичные поля в другую структуру |
|
порядок и строка запроса |
стабильный порядок, кодирование пробелов |
|
JSON-байты |
пробелы, ASCII, порядок ключей |
|
библиотека моделей |
чтение и запись моделей |
|
Не помещайте в проекцию авторизацию, капчу, CSRF и другие значения сессии. Их добавляют
Identity, зависимости и политики защиты после построения предметного документа.