I am currently throwing a custom Exception - RequestValidationException
.
ExceptionHandler
class:
@RestControllerAdvice
@Slf4j
public class RestExceptionHandler {
@ExceptionHandler(value = RequestValidationException.class)
@ResponseStatus(HttpStatus.PRECONDITION_FAILED)
public Mono<HttpValidationError> handleRequestValidationException(RequestValidationException exception) {
log.error("Received exception: ", exception);
List<String> loc = new ArrayList<>();
loc.add(exception.getMessage());
ValidationError validationError = ValidationError.builder()
.loc(loc)
.msg(exception.getMessage())
.build();
List<ValidationError> errorMessages = new ArrayList<>();
errorMessages.add(validationError);
return Mono.just(HttpValidationError.builder().detail(errorMessages).build());
}
RequestValidationException
class:
public class RequestValidationException extends RuntimeException {
public static final HttpStatus statusCode = HttpStatus.PRECONDITION_FAILED;
public RequestValidationException(String text) {
super(text);
}
public HttpStatus getStatusCode() {
return statusCode;
}
}
When the exception
is thrown, I want the following response
:
Code: 412
{
"detail": [
{
"loc": [
"No ID found to update. Please add an ID"
],
"msg": "No ID found to update. Please add an ID",
"type": null
}
]
}
What I am receiving is:
{
"error_code": 500,
"message": "No ID found to update. Please add an ID"
}
I checked the application logs and nowhere is the RestExceptionHandler
being called. It just logs this error
:
"level":"ERROR","logger":"c.a.c.c.c.AbstractController","thread":"boundedElastic-1","message":"Controller exception","stack":"<#384d845f> c.a.c.a.e.RequestValidationException
I just can't seem to figure out what's wrong with this code. Can someone point out what I might be missing? Thanks.
CodePudding user response:
I was only able to get this to work with an implementation of AbstractErrorWebExceptionHandler
as follows (sorry for the kotlin code):
@Component
@Order(-2)
class GlobalExceptionHandler(errorAttributes: ErrorAttributes,
resources: WebProperties.Resources,
applicationContext: ApplicationContext,
serverCodecConfigurer: ServerCodecConfigurer) : AbstractErrorWebExceptionHandler(errorAttributes, resources, applicationContext) {
companion object {
private val logger = KotlinLogging.logger {}
private const val HTTP_STATUS_KEY = "status"
private const val MESSAGE_KEY = "message"
private const val ERRORS_KEY = "errors"
}
init {
setMessageWriters(serverCodecConfigurer.writers)
}
override fun setMessageWriters(messageWriters: MutableList<HttpMessageWriter<*>>?) {
super.setMessageWriters(messageWriters)
}
override fun getRoutingFunction(errorAttributes: ErrorAttributes?): RouterFunction<ServerResponse> {
return RouterFunctions.route({ true }) { request ->
val error: Throwable = getError(request)
logger.error("Handling: ", error)
val errorProperties = getErrorAttributes(request, ErrorAttributeOptions.defaults())
when (error) {
is WebExchangeBindException -> {
....
}
else -> {
...
}
}
ServerResponse.status(HttpStatus.valueOf(errorProperties[HTTP_STATUS_KEY] as Int))
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(errorProperties)
}
}
}
In Java it would be something like:
@Component
@Order(-2)
public class GlobalExceptionHandler extends AbstractErrorWebExceptionHandler {
private static final String HTTP_STATUS_KEY = "status";
private static final String MESSAGE_KEY = "message";
private static final String ERRORS_KEY = "errors";
public GlobalExceptionHandler(ErrorAttributes errorAttributes, Resources resources, ApplicationContext applicationContext, ServerCodecConfigurer serverCodecConfigurer) {
super(errorAttributes, resources, applicationContext);
this.setMessageWriters(serverCodecConfigurer.getWriters());
}
public final void setMessageWriters(List messageWriters) {
super.setMessageWriters(messageWriters);
}
protected RouterFunction getRoutingFunction(ErrorAttributes errorAttributes) {
return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
}
private Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
Map<String, Object> errorPropertiesMap = getErrorAttributes(request,
ErrorAttributeOptions.defaults());
return ServerResponse.status(HttpStatus.BAD_REQUEST)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(errorPropertiesMap));
}
}
You can check more details at https://www.baeldung.com/spring-webflux-errors#global.
CodePudding user response:
I made a very trivial mistake of extending the controller
with AbstractController
class which was causing this issue. Removing it solved my problem.