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.
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>