How to properly propagate gRPC errors Spring-boot

Viewed 638

I have been following this article.

I am trying to handle gRPC errors gracefully within Spring-boot application, the main goal is to be able to get the error status in the gRPC client.

Following the article above, I am stuck on adding the interceptor for the exceptions. How do I do this in Spring-boot app? Through @Configuration ?

Or in general, how do you accomplish getting a proper error message through the client of a gRPC call?

2 Answers

You can use the interceptor while creation the Server, see its Github code.

Server server = ServerBuilder
    .forPort(8080)
    .addService(new GreetingService())
    .intercept(new ExceptionHandler())
    .build();

You could also switch to Lognet's Spring Boot gRPC lib to use @GRpcGlobalInterceptor.

To use other components in your interceptor declare it as @Component.

You can simplify the error handling if you can convert your gRPC application as Spring Boot using yidongnan/grpc-spring-boot-starter, then you can write @GrpcAdvice, similar to Spring Boot @ControllerAdvice as

@GrpcAdvice
public class ExceptionHandler {

  @GrpcExceptionHandler(ResourceNotFoundException.class)
  public StatusRuntimeException handleResourceNotFoundException(ResourceNotFoundException cause) {
    var errorMetaData = cause.getErrorMetaData();
    var errorInfo =
        ErrorInfo.newBuilder()
            .setReason("Resource not found")
            .setDomain("Product")
            .putAllMetadata(errorMetaData)
            .build();
    var status =
        com.google.rpc.Status.newBuilder()
            .setCode(Code.NOT_FOUND.getNumber())
            .setMessage("Resource not found")
            .addDetails(Any.pack(errorInfo))
            .build();
    return StatusProto.toStatusRuntimeException(status);
  }
}

On the client-side, you can catch this exception and unpack the registerUserResponse as: as

} catch (StatusRuntimeException error) {
   com.google.rpc.Status status = io.grpc.protobuf.StatusProto.fromThrowable(error);
   RegisterUserResponse registerUserResponse = null;
   for (Any any : status.getDetailsList()) {
     if (!any.is(RegisterUserResponse.class)) {
       continue;
     }
     registerUserResponse = any.unpack(ErrorInfo.class);
   }
   log.info(" Error while calling product service, reason {} ", registerUserResponse.getValidationErrorsList());
   //Other action
 }

I have written a detailed post about gRPC error handling - Getting Error Handling right in gRPC

Related