【问题标题】:Spring Boot gRPC: How to return error code when business error happens?Spring Boot gRPC:发生业务错误时如何返回错误码?
【发布时间】:2019-07-05 13:58:55
【问题描述】:

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

例如,当传递了不正确的参数时,我想返回 INVALID_ARGUMENT 错误代码。

如果我抛出一个自定义异常,它会以 io.grpc.StatusRuntimeException: UNKNOWN 结束。

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

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

【问题讨论】:

标签: java spring spring-boot grpc grpc-java


【解决方案1】:

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

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

TransmitStatusRuntimeExceptionInterceptor 将在回调期间捕获异常,如果是 StatusRuntimeException,则使用异常状态关闭调用。这与您的要求非常匹配,但默认情况下并未故意启用它。

【讨论】:

    【解决方案2】:

    我刚刚发表了一篇关于这个主题的文章Exception Handling and Error Propagation in 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);
                }
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

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

      披露:我是这个启动器的创造者。

      【讨论】:

        【解决方案4】:

        gRPC 不会传播错误。来自official documentation -

        创建具有给定原因的 Status 的派生实例。但是,原因并没有从服务器传输到客户端。

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

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

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

        我写了一篇关于error handling in gRPC的详细博文。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-11-16
          • 2018-07-15
          • 2017-08-07
          • 1970-01-01
          • 1970-01-01
          • 2018-07-07
          • 1970-01-01
          相关资源
          最近更新 更多