Search code examples
javaspring-bootvalidationexceptionenums

How to Return a Custom Error Message for Invalid Enum Value in Spring Boot RequestBody?


My put endpoint accept a requestBody of type UpdateUserDTO. The role property must be of type roleEnum. I am looking for a way to return a custom message if the role property provided is not a roleEnum.

endpoint :

@PutMapping("/administrate/{id}")
public UserDTO administrateUser(@PathVariable Long id, @RequestBody UpdateUserByAdminDTO requestBody) {}

DTO:

@Getter
@Setter
@NoArgsConstructor
public class UpdateUserByAdminDTO {
    private Boolean isBanned;
    private RoleEnum role;
}

For example, when request body is not properly set :

{
   "isBanned": false,
   "role":"USzdER"
}

I want to receive a 400 status error with message "role is invalid". I am not sure I can acquieve this with validation. Currently I don't understand why I receive a 403 error while user is logged and as appropriate permission.

Error from the logs: `Error I receive is:

DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type com.wcs.mtgbox.auth.domain.dto.RoleEnum from String "s": not one of the values accepted for Enum class: [USER, ADMIN]]


Solution

  • First, the 403 exception might be due to user permission issues. It would be best to check the UserDetails object and the Spring Security code to determine the exact cause.

    Here's a link to the issue I resolved; it might be helpful to take a look.


    Second, returning a custom message when the value is not an enum can be implemented in two ways.

    The first way is to set up an ExceptionHandler for HttpMessageNotReadableException.

    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity<String> handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) {
        Throwable cause = ex.getCause();
        if (cause instanceof InvalidFormatException invalidFormatException) {
            if (invalidFormatException.getTargetType().isEnum()) {
                return new ResponseEntity<>("role is invalid", HttpStatus.BAD_REQUEST);
            }
        }
        return new ResponseEntity<>("Invalid request body", HttpStatus.BAD_REQUEST);
    }
    

    HttpMessageNotReadableException occurs during the process of reading and mapping the HTTP request body in Spring MVC.

    InvalidFormatException is an exception that occurs during the deserialization of JSON data.

    This way, you can implement custom exception handling for cases where an invalid role is provided.


    The second way is to receive a value as a string, validate it, and occur an exception if it does not match.

    @Getter
    @AllArgsConstructor
    public enum RoleEnum {
        MEMBER("MEMBER"),
        ADMIN("ADMIN"),
        MANAGER("MANAGER");
        private final String provider;
    
        public static RoleEnum translateStringToRole(String role) {
            return switch (role.toLowerCase()) {
                case "member" -> RoleEnum.MEMBER;
                case "admin" -> RoleEnum.ADMIN;
                case "manager" -> RoleEnum.MANAGER;
                default -> throw new RoleEnumException("role is invalid.");
            };
        }
    }
    

    Exception Handling

    public class RoleEnumException extends RuntimeException {
        private final String message;
    
        public RoleEnumException(final String message) {
            this.message = message;
        }
    }
    
    @ExceptionHandler({
        RoleEnumException.class
    })
    public ResponseEntity<ExceptionResponse> handleRoleEnumException(final RoleEnumException e) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
            .body(new ExceptionResponse(e.getMessage()));
    }
    
    @Getter
    @RequiredArgsConstructor
    public class ExceptionResponse {
        private final String message;
    }
    

    I think this article will be helpful to you.

    Have a nice day - kevin