Отправка ответа со статусом «не в порядке» не допускается для унарного-унарного RPC (без потоковой передачи с обеих сторон). Для потокового RPC сервер может отправить ответ перед отправкой кода ошибки, но это не рекомендуется. Смешивание нормального ответа с состоянием ошибки может привести к будущим проблемам с ремонтопригодностью, например, если одна и та же ошибка относится к нескольким RPC, должны ли все ответные сообщения ProtoBuf включать эти поля?
Возвращаясь к вашему вопросу, «капчу-токен» следует рассматривать как часть состояния ошибки, поэтому его можно добавить как один из конечных метаданных. В вашем случае вы можете добавить сериализованное прото-сообщение в виде двоичных конечных метаданных, добавив суффикс -bin
в свой ключ конечных метаданных.
Также существует официальный поддерживаемый пакет grpcio-status
, который делает это за вас.
Серверная сторона упаковывает расширенный статус ошибки в прототипное сообщение "grpc_status.status_pb2.Status". В приведенном ниже примере просто используются прототипы с общей ошибкой, но вы можете упаковать «любой» прототип в details
, если ваш клиент их понимает.
# Server side
from grpc_status import rpc_status
from google.protobuf import any_pb2
def ...Servicer(...):
def AnRPCCall(request, context):
...
detail = any_pb2.Any()
detail.Pack(
rpc_status.error_details_pb2.DebugInfo(
stack_entries=traceback.format_stack(),
detail="Can't recognize this argument",
)
)
rich_status = grpc_status.status_pb2.Status(
code=grpc_status.code_pb2.INVALID_ARGUMENT,
message='Invalid argument',
details=[detail]
)
context.abort_with_status(rpc_status.to_status(rich_status))
# The method handler will abort
Клиентская сторона декодирует ошибки и реагирует на них.
# Client side
try:
self._channel.unary_unary(_ERROR_DETAILS).with_call(_REQUEST)
except grpc.RpcError as rpc_error:
status = rpc_status.from_call(rpc_error)
for detail in status.details:
if detail.Is(error_details_pb2.DebugInfo.DESCRIPTOR):
info = error_details_pb2.DebugInfo()
detail.Unpack(info)
# Handle the debug info
elif detail.Is(OtherErrorProto.DESCRIPTOR):
# Handle the other error proto
else:
# Handle unknown error
Подробнее о статусе rich: https://github.com/grpc/proposal/blob/master/L44-python-rich-status.md