Hi Please find my answer in 2 steps, first the correct and then the second the suggested way to implement.
Correction:
Please use ObjectUtils.isEmpty(arg)
for checking if string is 0 length or null. Here is the modified version of your code
public interface ZipcodeService {
void validate(@Zipcode String zipCode) throws ZipCodeValidationException;
}
@Service
public static class ZipcodeServiceImpl implements ZipcodeService {
private final ZipCodeRegexMatcher zipCodeRegexMatcher;
public ZipcodeServiceImpl() {
zipCodeRegexMatcher = new ZipCodeRegexMatcher();
}
@Override
public void validate(String zipCode) throws ZipCodeValidationException {
// uncomment for Regex Validation
// boolean valid = zipCodeRegexMatcher.isValid(zipCode);
// uncomment for Simple text validation
final boolean valid = !ObjectUtils.isEmpty(zipCode);
if (!valid) {
throw new ZipCodeValidationException("Invalid zipcode");
}
}
}
This is how the caller looks like from Controller
@GetMapping(path = "dummy")
public String getDummy(@RequestParam("zipcode") String zipCode) {
try {
zipcodeService.validate(zipCode);
return zipCode;
} catch (ZipCodeValidationException e) {
return e.getMessage();
}
}
Suggested way:
add following entry to pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Create Annotation and Validator as given below
@Constraint(validatedBy = {ZipcodeValidator.class})
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Zipcode {
String message() default "Invalid Zipcode value";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public static class ZipcodeValidator implements ConstraintValidator<Zipcode, String> {
private final ZipCodeRegexMatcher zipCodeRegexMatcher;
public ZipcodeValidator() {
zipCodeRegexMatcher = new ZipCodeRegexMatcher();
}
@Override
public boolean isValid(String zipCode, ConstraintValidatorContext constraintValidatorContext) {
return zipCodeRegexMatcher.isValid(zipCode);
}
}
Once this setup is done, head over to Controller class and annotated class with
@Validated and field you want to have validation on with the Custom Annotation i.e Zipcode
we have just created. We are creating a Custom Validator in this case ZipcodeValidator by extending ConstraintValidator.
This is how the caller looks like:
@GetMapping
public String get(@Zipcode @RequestParam("zipcode") String zipCode) {
return zipCode;
}
On Failed validation, it throws javax.validation.ConstraintViolationException: get.zipCode: Invalid Zipcode value
which you can customize according to your need by using ControllerAdvice.
You can also use @Zipcode
annotation at the service level and it works the same way. Regarding ZipCodeRegexMatcher
instead of creating it inside the constructor you can create a bean and inject that dependency. It is a simple class that has regex for zipcode and performs validation.
public static class ZipCodeRegexMatcher {
public static final String ZIP_REGEX = "^[0-9]{5}(?:-[0-9]{4})?$";
private final Pattern pattern;
public ZipCodeRegexMatcher() {
pattern = Pattern.compile(ZIP_REGEX);
}
public boolean isValid(String zipCode) {
return pattern.matcher(zipCode).matches();
}
}
The entire code is located here