Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Boot ExceptionHandler for MethodArgumentNotValidException in RestController never gets invoked

I'm using Spring Boot v2.2.10 and Spring v5.2.9. I've created a Rest controller with an @Valid @RequestBody annotation. The validation works fine (I get a 400 BAD REQUEST response when submitting values outside of the constraints), but I don't like the default error message as it reveals too much of internals. Thus, I defined an @ExceptionHandler(MethodArgumentNotValidException.class) method, but this method never gets invoked.

This is the controller with the exception handler:

import java.util.List;
import java.util.stream.Collectors;

import javax.validation.Valid;

import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import lombok.RequiredArgsConstructor;


@RestController
@RequiredArgsConstructor
@RequestMapping("/api/data")
public class DataController {

    @PostMapping(path = "/{id}", consumes = MediaType.APPLICATION_JSON_VALUE)
    private void postData(
            @PathVariable String id,
            @Valid @RequestBody MyDTO dto) {
        
        // DO SOMETHING

    }
    
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public List<String> handleValidationExceptions(MethodArgumentNotValidException ex) {
        System.out.println("Handling method argument not valid exception");
        return ex.getBindingResult()
            .getAllErrors().stream()
            .map(ObjectError::getDefaultMessage)
            .collect(Collectors.toList());
    } 
}

The DTO:

import javax.validation.constraints.Max;
import lombok.Getter;

@Getter
public class MyDTO {
    
    @Max(value = 1, message = "Value should not be greater than 1")
    private float value;
    
}

I also tried to put the ExceptionHandler into an extra class:

// Imports

@Order(Ordered.HIGHEST_PRECEDENCE)
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    // Same as before

}

As I've said, my ExceptionHandler never gets invoked. What might I be doing wrong?

like image 271
AnjaM Avatar asked Nov 23 '25 03:11

AnjaM


2 Answers

try extend from class ResponseEntityExceptionHandler and override handleMethodArgumentNotValid

@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
@Slf4j
public class ExceptionController extends ResponseEntityExceptionHandler {

@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
        HttpHeaders headers, HttpStatus status, WebRequest request) {
    return ...;
}
like image 173
Carlos Avatar answered Nov 25 '25 18:11

Carlos


You can try to apply the same approach in this link: https://www.baeldung.com/spring-boot-bean-validation

I got the same problem and I solved it like in the code below which is slightly modified version of the sample in the link.

I think the problem is in the return type of your handleValidationExceptions method. Instead of List you should use Map<>.

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, String>> handleValidationExceptions(
            MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getAllErrors().forEach((error) -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });
        return ResponseEntity.badRequest().body(errors);
    }
}

Because I used @ControllerAdvice instead of @RestControllerAdvice, I needed to return ResponseEntity type. And I determine the response type in my return like ResponseEntity.badRequest().body(errors); But if you want to use RestControllerAdvise annotation then you can directly return Map<> object and in this case you can keep using @ResponseStatus(HttpStatus.BAD_REQUEST) over your exception handler method. So try to apply the code in your GlobalExceptionHandler class.

like image 44
Meriç Akgül Avatar answered Nov 25 '25 20:11

Meriç Akgül



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!