tentrun
@tentrun
Clown c# inside

Почему gRPC подменяет исключения?

Суть вопроса такова - есть шлюз куда приходит запрос, далее он вызывает необходимый сервис
В шлюзе есть глобальный перехватчик исключений

Есть сервис, в котором выбрасывается исключение
Исключение
public class RequiredPropertyIsNullOrEmptyException : RpcException
{
    private string _requestId;
    private string _originalRequestId;

    public string RequestId
    {
        get { return _requestId; }

        private set { _requestId = value; }
    }
    public string? OriginalRequestId
    {
        get { return _originalRequestId; }

        private set { _originalRequestId = value; }
    }

    /// <summary>
    /// Конструктор ошибки
    /// </summary>
    /// <param name="status">Статус ошибки</param>
    /// <param name="requestId">Уникальный идентификатор запроса, в котором произошла ошибка</param>
    /// <param name="originalRequestId">Уникальный идентификатор запроса из внешней системы</param>
    /// <param name="trailers">Клеймсы</param>
    public RequiredPropertyIsNullOrEmptyException(Status status, string requestId, string? originalRequestId) : base(status)
    {
        _requestId = requestId;
        _originalRequestId = originalRequestId ?? string.Empty;
    }
}

В самом сервисе оно содержит информацию и поля и оно спокойно гуляет по сервису, однако, выходя за контекст этого сервиса и возвращаясь в шлюз что-то меняет исключение на базовый RpcException, соответственно стак трейс и данные теряются, но, передается Status и я могу туда пробросить один Response Trailer (видимо там ограничение на 3 элемента стоит, ибо, если их больше 3 - grpc отдает уже ответ 500), но, не хотелось бы туда засовывать огромный json и потом его разбирать.
Собственно, почему это происходит? И как с этим бороться?
p.s. я знаю что у grpc всего одно исключение
Сам интерцептр
public class GrpcErrorHandler : Interceptor
{
    public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
        TRequest request,
        ServerCallContext context,
        UnaryServerMethod<TRequest, TResponse> continuation)
    {
        try
        {
            return await continuation(request, context);
        }
        catch (Exception e)
        {

            switch (e)
            {
                case RequiredPropertyIsNullOrEmptyException requiredPropertyIsNullOrEmptyException:
                    context.ResponseTrailers.Add(new Metadata.Entry("requestId", requiredPropertyIsNullOrEmptyException?.Data?["requestId"]?.ToString() ?? ""));
                    context.ResponseTrailers.Add(new Metadata.Entry("originalRequestId", requiredPropertyIsNullOrEmptyException?.Data?["originalRequestId"]?.ToString() ?? "")); //почему-то есть в контексте сервиса, но, выходя за рамки - теряется.
                    throw new RequiredPropertyIsNullOrEmptyException(requiredPropertyIsNullOrEmptyException!.Status, requiredPropertyIsNullOrEmptyException?.Data?["requestId"]?.ToString() ?? "", requiredPropertyIsNullOrEmptyException?.Data?["originalRequestId"]?.ToString());
            }
            throw;
        }
    }
}
  • Вопрос задан
  • 82 просмотра
Решения вопроса 1
AshBlade
@AshBlade Куратор тега C#
Просто хочу быть счастливым
А откуда другой сервис знает, какие КЛАССЫ/ОБЪЕКТЫ есть в другом сервисе?
Если нужно кидать такое-же исключение на сервисе клиенте, то нужно это исключение там и создать.

А вообще, для своих типов исключений необходимо использовать GRPC_STATUS_UNKNOWN. В документации так и написано:
Server threw an exception (or did something other than returning a status code to terminate the RPC)


Решение только одно - в каждом сервисе писать свои обработчики таких исключений и свои классы исключений
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы