Search code examples
javaspringvalidationthymeleaf

Spring MVC validation and Thymeleaf - validating Integer field


I am moving .net project to Spring Boot. So the question is on how to properly validate Integer fields in Spring. I have an entity with an Integer field:

@Entity
@Table(name = "tb_employee")
public class EmployeeDev {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "empl_id")
    private int emplId;
        
    @Range(min = 10, max = 50, message="Numbers only between 10 and 50")
    @Column(name = "default_vacation_days", nullable = true)
    private Integer defaultVacationDays;

... and a controller capturing the errors:

// update employee
    @PostMapping("/edit")
    public String showFormForUpdate(@Valid @ModelAttribute("employee") EmployeeDev employee, Errors errors,
            RedirectAttributes redirectAttributes,
            Model theModel) {

        if (null != errors && errors.getErrorCount() > 0) {
            
            List<ObjectError> errs = errors.getAllErrors();
            String errMsg = "";
            
            for (ObjectError e :errs)
                errMsg += e.getDefaultMessage();
            
            
            theModel.addAttribute("message", "Employee Edit failed. " + errMsg  );
            theModel.addAttribute("alertClass", "alert-danger");
            return "employeesdev/employee-form-edit";
        }

Now the problem is when I type into the default vacation days field any number outside of the range it shows the correct validation message: Numbers only between 10 and 50.

However if I try to insert something like 1A (possible user typo) I get this message: Failed to convert property value of type java.lang.String to required type java.lang.Integer for property defaultVacationDays; nested exception is java.lang.NumberFormatException: For input string: "1A"

I understand this is the correct message but I hate to show a message like this to a user. I would prefer to show just "Numbers only between 10 and 50" instead of data type conversion problems. Why bother users with Java data types?

I would appreciate any suggestions.


Solution

  • If you want get custom behaviour from the annotation you need to define your own constriant annotation and validator for this annotation.

    Here is basic example of custom constraint annotation:

    @Target({TYPE, ANNOTATION_TYPE})
    @Retention(RUNTIME)
    @Constraint(validatedBy = CheckCalculationTypeValidator.class)
    @Documented
    public @interface CheckCalculationType {
    
       String message() default "calculation_type shall be not NULL if status = active";
    
       Class<?>[] groups() default {};
    
       Class<? extends Payload>[] payload() default {};
    }
    

    and validator:

        public class CheckCalculationTypeValidator implements ConstraintValidator<CheckCalculationType, RequestDto> {
    
        @Override
        public boolean isValid(RequestDto dto, ConstraintValidatorContext constraintValidatorContext) {
            if (dto == null) {
                return true;
            }
            return !(Status.ACTIVE.equals(dto.getStatus()) && dto.getCalculationType() == null);
        }
    
        @Override
        public void initialize(CheckCalculationType constraintAnnotation) {
            // NOP
        }
    }
    

    Required dependency for Hibernate Validator:

    <dependency>
        <groupId>org.hibernate.validator</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>6.0.2.Final</version>
    </dependency>