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

时间:2019-07-05 13:58:55

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

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

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

如果我抛出自定义异常,则其结尾为io.grpc.StatusRuntimeException: UNKNOWN

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

不幸的是,该项目中没有太多的文档。

4 个答案:

答案 0 :(得分:2)

gRPC阻止您抛出异常以将错误传达给用户。这是因为意外泄漏您可能没有考虑发送给客户端的信息很简单。

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

TransmitStatusRuntimeExceptionInterceptor会在回调过程中捕获异常,如果它是StatusRuntimeException,请以异常状态关闭呼叫。这与您的要求非常匹配,但是默认情况下没有故意启用它。

答案 1 :(得分:0)

我刚刚发表了关于该主题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);
            }
        }
    }
}

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

gRPC 不会传播错误。从 official documentation -

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

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

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

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

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