Описать протокол с конвертом#
Конверт не является сетевым транспортом.
Он добавляет к полезной нагрузке method, correlation id и служебные поля. Конверт принадлежит сервису, поэтому объявляется на роутере и применяется ко всем его RPC-операциям.
# examples/docs/protocols.py
class BillingService:
protocol = JsonRpc(path="/rpc")
@dataclass(frozen=True, slots=True, kw_only=True)
class Charge(RpcOperation[Receipt]):
__http__ = Rpc.method("billing.charge")
account: JsonField[str]
amount: JsonField[int]
class BillingApi(BillingService, AsyncApi):
charge = op(Charge)
async def main() -> None:
site = GuideSite()
async with async_guide_client(site) as client:
receipt = await BillingApi(client).charge(account="account-7", amount=1290)
print(f"receipt: {receipt.reference}")
print(site.calls[0])
JsonRpc(path="/rpc") выбирает общий HTTP-путь. Rpc.method(...) задаёт имя внутри тела, а поля
операции становятся params. Публичный вызов остаётся обычным типизированным методом.
Файл целиком
"""Call a JSON-RPC service through the shared HTTP runtime."""
import asyncio
from dataclasses import dataclass
from pydantic import BaseModel
from eazy_sdk import AsyncApi, JsonField, op
from eazy_sdk.protocols import JsonRpc, Rpc, RpcOperation
from examples.docs.guide_site import GuideSite, async_guide_client
class Receipt(BaseModel):
reference: str
# region docs: protocols
# examples/docs/protocols.py
class BillingService:
protocol = JsonRpc(path="/rpc")
@dataclass(frozen=True, slots=True, kw_only=True)
class Charge(RpcOperation[Receipt]):
__http__ = Rpc.method("billing.charge")
account: JsonField[str]
amount: JsonField[int]
class BillingApi(BillingService, AsyncApi):
charge = op(Charge)
async def main() -> None:
site = GuideSite()
async with async_guide_client(site) as client:
receipt = await BillingApi(client).charge(account="account-7", amount=1290)
print(f"receipt: {receipt.reference}")
print(site.calls[0])
# endregion docs: protocols
if __name__ == "__main__":
asyncio.run(main())
receipt: receipt-42
POST api.mail.example/rpc
Место конверта в запросе#
модель → проекция → конверт → кодирование → шифрование байтов → подпись
Подпись и шифрование всего payload получают конверт целиком. Значение JSON-RPC id по умолчанию
сохраняется при retry, чтобы сервер мог распознать дубликат. Для сервиса, который требует новый
идентификатор, укажите id_on_retry="regenerate".
JSON-RPC обычно сообщает ошибку внутри ответа 200. rpc_result(...), rpc_error(...) и
rpc_error_default(...) превращают result и error в обычные response cases, поэтому вызывающий
получает тот же ApiError, что и у REST.
Пакетный JSON-RPC вызов не входит в прозрачный runtime. Частичный успех нарушает соответствие одной операции одному типизированному результату, а повтор всего batch больше нельзя считать безопасным только по свойствам отдельных элементов.