Quarkus Reactive Mutiny - Composite Exception encapsulation on Runtime Exceptions and @ExceptionHandler
See original GitHub issueDescription
I am evaluating quarkus 1.6 and reactive Mutiny for reactive routes. My experience with quarkus mutiny exception handling is not so great . The current problem I have is if I throw a RuntimeException down in the service layer or data layer . The exception gets encapsulated as a CompositeException with a list of causes where one of the causes is the the RuntimeException thrown. This means that I have to always Parse the CompositeException to retrieve the actual exception which I have thrown to determine the HTTP status response ( of whether to throw a 500 or 422 or something else) .
Code Snippet - Current :
return Uni.createFrom().item(getRequest).flatMap(request ->{
log.info("Request Received successfully {}", kv("someRequest", request));
return xyzService.get(request, transactionContext);
}).onItemOrFailure().apply((paList, throwable) ->{
if (paList != null) {
return Response.ok(somePOJO).build();
}
if (throwable != null) {
if (throwable instanceof CompositeException) {
CompositeException compositeException = (CompositeException) throwable;
Optional < Throwable > exception = isExceptionInstanceOf(compositeException, XYZRuntimeException.class);
if (exception.isPresent()) {
return Response.serverError().status(500).entity(somePOJO).build();
}
}
if (throwable instanceof CircuitBreakerOpenException) {
return Response.serverError().status(500).entity(somePOJO).build();
}
}
return Response.serverError().status(500).entity(somePOJO).build();
});
Code Snippet - Expected :
//Controller logic
return Uni.createFrom().item(getRequest)
.flatMap(request -> {
log.info("Request Received successfully {}", kv("someRequest", request));
return xyzService.get(request, transactionContext);
})
.onItem()
.apply(paList -> {
if (paList != null) {
return Response.ok(somePOJO)
.build();
}
return Response.serverError()
.status(500)
.entity(somePOJO)
.build();
});
//Exception Handler can be in the same class or a different class ( different class will be annotated as @ControllerAdvice)
@ExceptionHandler
public Uni<Response> handleXYZException(XYZRuntimeException exception, <<FrameworkContextObject>> obj){
return Response.serverError()
.status(500)
.entity(somePOJO)
.build();
}
Implementation ideas
Can we have something similar to spring webflux , where if a RuntimeException is thrown from the data layer
a) Exactly same exception is captured in the error block of the controller class without needing to parse an encapsulated exception .
b) The Spring Boot framework has a @ControllerAdvice and @ExceptionHandler annotations to cleanly segregating the class for handling exceptions and controller. Can the same be available for Quarkus Reactive Mutiny extension.
c) The Spring boot framework has ServerWebExchange object which we can use as a context object to pass custom context . The good thing of this serverwebExchange object is that it is injected automatically as part of @ExceptionHandler Method making it easy to pass Context objects to exception handling methods . Can we have something similar in quarkus / reactive mutiny ?
(If you have any implementation ideas, they can go here, however please note that all design change proposals should be posted to the Quarkus developer mailing list (or the corresponding Google Group; see the decisions process document for more information).
Issue Analytics
- State:
- Created 3 years ago
- Comments:22 (11 by maintainers)
Thanks @cescoffier . that works .
@mkouba : Is there a plan on adding this feature for exception type based method resolvers in the next 1,2 or 3 month roadmap for vertx-io web ?
CompositeExceptions are only created if your first failure (the RuntimeException) triggers a second failure. So, you have at least 2 failures.
Another situation where
CompositeException
is used is when you combine multiple actions and explicitly require to collect all the failures. In this case, if there is more than one aCompositeException
is used. Again, you have multiple failures.Note that you should be able to retrieve the initial “main” failure using
getCause()
.