Home > Back-end >  @RestControllerAdvice not working in Spring Boot reactive java application
@RestControllerAdvice not working in Spring Boot reactive java application

Time:10-27

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.

  • Related