Spring Boot gRPC:业务发生错误时如何返回错误码?

问题描述 投票:0回答:5

我正在使用 LogNet grpc-spring-boot-starter 实现 gRPC API。

例如,当传递了不正确的参数时,我想返回

INVALID_ARGUMENT
错误代码。

如果我抛出自定义异常,最终会出现

io.grpc.StatusRuntimeException: UNKNOWN

问:是否可以定义一些异常处理机制,以便特定类型的异常始终会导致正确的 gRPC 状态?

不幸的是,项目中没有那么多文档。

java spring spring-boot grpc grpc-java
5个回答
6
投票

gRPC 不鼓励您抛出异常以将该错误传达给用户。这是因为意外泄露您可能没有考虑过发送给客户的信息是微不足道的。

相反,我们鼓励您将

StatusException
StatusRuntimeException
传递给
streamObserver.onError(Throwable)
。如果您在自己的代码中使用异常来传达此信息,则可以在代码中放置一个 try-catch 并将异常传递给
onError()
。例如,这对于
StatusException
来说可能是公平的,因为它是一个受检查的异常。

TransmitStatusRuntimeExceptionInterceptor 它将在回调期间捕获异常,如果它是

StatusRuntimeException
,则使用异常状态关闭调用。这与您的要求非常匹配,但默认情况下并未故意启用它。


1
投票

我刚刚发表了一篇关于此主题的文章gRPC Java 中的异常处理和错误传播

您可以使用拦截器处理异常,例如:

public class ExceptionHandler implements ServerInterceptor {

    @Override
    public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> serverCall, Metadata metadata,
                                                                 ServerCallHandler<ReqT, RespT> serverCallHandler) {
        ServerCall.Listener<ReqT> listener = serverCallHandler.startCall(serverCall, metadata);
        return new ExceptionHandlingServerCallListener<>(listener, serverCall, metadata);
    }

    private class ExceptionHandlingServerCallListener<ReqT, RespT>
            extends ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT> {
        private ServerCall<ReqT, RespT> serverCall;
        private Metadata metadata;

        ExceptionHandlingServerCallListener(ServerCall.Listener<ReqT> listener, ServerCall<ReqT, RespT> serverCall,
                                            Metadata metadata) {
            super(listener);
            this.serverCall = serverCall;
            this.metadata = metadata;
        }

        @Override
        public void onHalfClose() {
            try {
                super.onHalfClose();
            } catch (RuntimeException ex) {
                handleException(ex, serverCall, metadata);
                throw ex;
            }
        }

        @Override
        public void onReady() {
            try {
                super.onReady();
            } catch (RuntimeException ex) {
                handleException(ex, serverCall, metadata);
                throw ex;
            }
        }

        private void handleException(RuntimeException exception, ServerCall<ReqT, RespT> serverCall, Metadata metadata) {
            if (exception instanceof IllegalArgumentException) {
                serverCall.close(Status.INVALID_ARGUMENT.withDescription(exception.getMessage()), metadata);
            } else {
                serverCall.close(Status.UNKNOWN, metadata);
            }
        }
    }
}

0
投票

gRPC 不会传播错误。来自官方文档 -

使用给定原因创建 Status 的派生实例。然而,原因并没有从服务器传输到客户端。

如果您想将自定义信息从服务器传递到客户端,那么您有几个选项 -

  1. 使用元数据将错误信息从服务器传播到客户端
  2. 使用
    google.rpc.Status
    repeated google.protobuf.Any details
  3. 中传递错误详细信息

您需要捕获这两种情况下的异常,准备错误消息并将其发送回客户端。

我写了一篇关于 gRPC 中的错误处理的详细博客文章。


0
投票

参考- Spring Boot + gRPC 错误处理示例
在 gRPC 服务器端,我们将状态代码返回给客户端。

 if ((request.getAccountNumber().equals("account5"))) {
            responseObserver.onError((Status.INVALID_ARGUMENT.withDescription("The requested Account Number cannot be found."))
                        .asRuntimeException());
                return;
            }

在客户端 - 我们捕获 StatusRuntimeException。从异常中,我们获取状态并打印代码,该代码将显示为 INVALID_ARGUMENT

catch (StatusRuntimeException ex) {
            Status status = ex.getStatus();
            System.out.println("error code -" + status.getCode());
            System.out.println("error description -" + status.getDescription());
}

-1
投票

starter 的最新版本集成了 spring 验证支持。如果验证失败,它将返回 INVALID_ARGUMENT。

披露:我是这个开胃菜的创造者。

© www.soinside.com 2019 - 2024. All rights reserved.