Request API#
Маркер размещает поле операции. Path[T] и Annotated[T, markers.Path()] являются двумя
записями одной аннотации. Имя wire-поля передаётся маркеру явно (markers.Query("perPage")) либо объявляется
моделью (serialization_alias у Pydantic, msgspec.field(name=...)); без этого используется
Python-имя.
Плоское object-shaped тело состоит из полей самой операции с маркером JsonField, Form или Part;
готовые bytes помечаются markers.BytesBody(). Dataclass, Pydantic v2 и msgspec под
markers.JsonBody() оставляйте для вложенных документов и правил валидации, которыми
действительно должна владеть модель.
from dataclasses import dataclass
from eazy_sdk import Http, HttpOperation, JsonField, SyncApi, op
@dataclass(frozen=True, slots=True, kw_only=True)
class CreateUser(HttpOperation[User]):
__http__ = Http.post("/users")
name: JsonField[str]
class UsersApi(SyncApi):
create = op(CreateUser)
Caller передаёт create(name="Ada"); type checker видит конструктор класса со всеми его
значениями по умолчанию.
Если публичные поля должны остаться плоскими, а сервер ждёт другой nested document, объявите
projection=BodyProjection(target=..., using=..., encoding=...) в __http__. Источником служит сама
операция: поля без маркера и есть вход mapper-а. Mapper строит private target на каждой attempt,
target проходит model adapter validation, и только после этого выбранный encoding (Body.json(),
form/multipart или custom BodyCodec) кодирует один semantic document. Mapper не получает client/transport state; если
ему нужны значения зависимостей, объявите requires= и второй параметр using(source, injected).
Задача |
Форма |
Поведение отсутствующего значения |
|---|---|---|
Значение по умолчанию |
|
default материализуется до projection |
«Не отправлять вовсе» |
|
поле не попадает на провод |
Обязательное значение |
Поле без default |
|
Явно переданный |
Nullable-тип поля |
значение присутствует и равно |
Wire фиксирует представление запроса на проводе, то есть всё, что влияет на байты, и объявляется там
же, где остальной контракт операции. CallOptions | None служит зарезервированным keyword-only
параметр управления одним вызовом.
Placement markers#
Marker |
Default wire name |
Основные настройки |
|---|---|---|
|
имя поля |
|
|
имя поля |
|
|
имя поля |
|
|
имя поля |
|
|
адрес запроса целиком |
|
Url стоит и на месте пути, и на поле: Http.get(markers.Url(hosts=("*.example",))) вместе с
url: Url[str]. Адрес приходит аргументом вызова; ссылка на хост вне списка отклоняется до
подготовки запроса, относительная резолвится против адреса сервиса. Path-полей у такой
операции нет, а объявленное Query-поле делает запрос владельцем query.
Body markers#
Marker |
Content type |
Назначение |
|---|---|---|
|
|
Вложенная модель, scalar или array |
|
|
Одно JSON field |
|
form-urlencoded |
Root, nested или model-owned document |
|
form-urlencoded |
Одно form field |
|
multipart/form-data |
Root, nested или model-owned document |
|
multipart/form-data |
Одна part |
|
заданный caller-ом |
Buffered bytes |
|
заданный caller-ом |
Повторно создаваемый stream |
Кодирование тела#
Имена выше являются маркерами поля: body: JsonBody[Payload] размещает значение. Тот же контракт в
encoding= у BodyProjection записывается через Body: один импорт на все форматы, как Http
на все методы:
Кодирование |
Дескриптор |
Тип содержимого |
|---|---|---|
|
|
|
|
|
form-urlencoded |
|
|
multipart/form-data |
|
|
задан вызывающим кодом |
|
|
задан вызывающим кодом |
from eazy_sdk import Body, BodyProjection, Http, HttpOperation
@dataclass(frozen=True, slots=True, kw_only=True)
class RegisterUser(HttpOperation[RegisteredUser]):
__http__ = Http.post(
"/register",
success={201: RegisteredUser},
projection=BodyProjection(
target=RegisterUserWire,
using=register_to_wire,
encoding=Body.json(),
),
)
Каждый метод принимает content_type=, поэтому vendor media type объявляется на месте:
Body.json(content_type="application/vnd.api+json").
BodyProjection(...) не является ещё одним body marker. Это связь публичных полей операции с
private wire schema и одним из перечисленных encodings; смешивать projection с root/flat body
нельзя. Источником по умолчанию служит сам класс операции: в проекцию идут поля без маркера размещения,
уже с применёнными значениями по умолчанию.
Ошибки binding и projection#
Public binding, projection и preparation failures используют один
eazy_sdk.core.errors.OperationBindingError.
Tooling читает поля ошибки напрямую, не разбирает message:
Поле |
Значение |
|---|---|
|
Стабильный machine-readable вид ошибки |
|
Public identity операции |
|
Caller field или nested path, если он известен |
|
|
error.as_dict() возвращает только эти безопасные поля. Значение аргумента, private wire document,
credentials и internal slot name в message, repr и mapping не входят.
Для источника проекции три состояния не смешиваются. Поле, оставшееся UNSET, в input проекции не
попадает. Явный None попадает. Отсутствие обязательного поля приводит к TypeError конструктора
операции, до проекции, providers и handler. Проекция получает значения этой attempt, включая retry
и redirect.
Для плоского JSON, form или multipart document объявляйте поля операции маркерами JsonField,
Form или Part. Не оборачивайте несколько scalar fields в Pydantic, dataclass или msgspec model.
Root body markers нужны для scalar/array payload, вложенного document или случая, когда модель
действительно владеет validation и serialization policy.
Wire принимает encrypted, order (FieldOrder(query=…, header=…, cookie=…, body=…)), exact, encoding (JsonPolicy), query (QueryCodec) и transport; проекция
объявляется отдельным параметром projection=. Она описывает представление, а не байты. Каждое поле
None означает «наследовать», поэтому операция, объявившая только encoding, сохраняет
шифрование своего сервиса. exact и transport проверяются как capability транспорта до
resolver calls и сетевых side effects.
Custom BodyCodec можно использовать вместо root body marker или как encoding BodyProjection:
он получает уже адаптированные semantic primitives и EncodeContext(operation_id=...), затем
возвращает окончательные bytes. Model registry в context отсутствует. ScalarCodec в placement
marker обязан вернуть одно wire value; повторяющиеся query names не поддерживаются.