Spring Boot gRPC: 发生业务错误时如何return 错误代码?
Spring Boot gRPC: How to return error code when business error happens?
我正在使用 LogNet grpc-spring-boot-starter 实现 gRPC API。
我想 return,例如,当传递了一个不正确的参数时,一个 INVALID_ARGUMENT
错误代码。
如果我抛出自定义异常,它会以 io.grpc.StatusRuntimeException: UNKNOWN
.
结束
问:是否可以定义一些异常处理机制,以便特定类型的异常始终导致正确的 gRPC 状态?
不幸的是,项目中没有那么多文档。
gRPC 不鼓励您抛出异常以将该错误传达给用户。这是因为意外泄露您可能没有考虑发送给客户的信息是微不足道的。
相反,我们鼓励您将 StatusException
或 StatusRuntimeException
传递给 streamObserver.onError(Throwable)
。如果您使用异常在您自己的代码中传达此信息,您可以在您的代码中放置一个 try-catch 并将异常传递给 onError()
。例如,这对于 StatusException
可能是公平的,因为它是一个已检查的异常。
TransmitStatusRuntimeExceptionInterceptor 将在回调期间捕获异常,如果它是 StatusRuntimeException
,则关闭异常状态的调用。这与您的要求非常匹配,但默认情况下并没有故意启用它。
我刚刚发表了一篇关于这个主题的文章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);
}
}
}
}
最新版本的启动器集成了spring验证支持。它 returns INVALID_ARGUMENT 如果验证失败。
披露:我是这个开胃菜的创造者。
gRPC 不会传播错误。来自 official documentation -
使用给定原因创建 Status 的派生实例。但是,原因是没有从服务器传输到客户端。
如果您想将自定义信息从服务器传递到客户端,那么您有两个选择 -
- 使用元数据将错误信息从服务器传播到客户端
- 使用
google.rpc.Status
在 repeated google.protobuf.Any details
中传递错误详细信息
您需要捕获这两种情况下的异常,准备一条错误消息,并将其发送回客户端。
我写了一篇关于error handling in gRPC的详细博客post。
我正在使用 LogNet grpc-spring-boot-starter 实现 gRPC API。
我想 return,例如,当传递了一个不正确的参数时,一个 INVALID_ARGUMENT
错误代码。
如果我抛出自定义异常,它会以 io.grpc.StatusRuntimeException: UNKNOWN
.
问:是否可以定义一些异常处理机制,以便特定类型的异常始终导致正确的 gRPC 状态?
不幸的是,项目中没有那么多文档。
gRPC 不鼓励您抛出异常以将该错误传达给用户。这是因为意外泄露您可能没有考虑发送给客户的信息是微不足道的。
相反,我们鼓励您将 StatusException
或 StatusRuntimeException
传递给 streamObserver.onError(Throwable)
。如果您使用异常在您自己的代码中传达此信息,您可以在您的代码中放置一个 try-catch 并将异常传递给 onError()
。例如,这对于 StatusException
可能是公平的,因为它是一个已检查的异常。
TransmitStatusRuntimeExceptionInterceptor 将在回调期间捕获异常,如果它是 StatusRuntimeException
,则关闭异常状态的调用。这与您的要求非常匹配,但默认情况下并没有故意启用它。
我刚刚发表了一篇关于这个主题的文章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);
}
}
}
}
最新版本的启动器集成了spring验证支持。它 returns INVALID_ARGUMENT 如果验证失败。
披露:我是这个开胃菜的创造者。
gRPC 不会传播错误。来自 official documentation -
使用给定原因创建 Status 的派生实例。但是,原因是没有从服务器传输到客户端。
如果您想将自定义信息从服务器传递到客户端,那么您有两个选择 -
- 使用元数据将错误信息从服务器传播到客户端
- 使用
google.rpc.Status
在repeated google.protobuf.Any details
中传递错误详细信息
您需要捕获这两种情况下的异常,准备一条错误消息,并将其发送回客户端。
我写了一篇关于error handling in gRPC的详细博客post。