Let’s say we want to task the back-end REST API application with validating the syntax of an email address provided by a new user while doing the registration. Aside from the fact that the “first level” validation should always happen on the front-end side (using for example a JavaScript solution) it is still a good idea to do a “second level” validation on the back-end (a user can have JavaScript support disabled in his/her browser or try some dirty hacks on our webpage and be sending invalid data to the server causing inconsistencies at best).
What would be a nice approach to handle server-side validation? A custom annotation – something like @ValidEmailSyntax
Here’s an idea on how to implement it:
1) Create an interface for the Validation Annotation:
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = EmailSyntaxValidator.class) @Documented public @interface ValidEmailSyntax { String message() default "Invalid email syntax"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; }
- As you can see from the code above, our @ValidEmailSyntax annotation can be used to annotate a field, method or another annotation
- “Invalid email syntax” is the message returned by the server in case of failed validation
2) Create a EmailSyntaxValidator class that will be responsible for making decision whether the subject of validation (field, method, etc…) is valid or not:
@Component public class EmailSyntaxValidator implements ConstraintValidator<ValidEmailSyntax, EmailAddressType> { protected final Logger logger = LoggerFactory.getLogger(getClass()); @Override public void initialize(ValidEmailSyntax constraintAnnotation) { logger.debug("EmailSyntaxValidator initialization successful"); } @Override public boolean isValid(EmailAddressType value, ConstraintValidatorContext context) { return value == null || Pattern.compile(EmailStructure.PATTERN, Pattern.CASE_INSENSITIVE).matcher(value.getEmail()).matches(); } }
3) The validator class above will validate the the email represented by following EmailAddressType object:
@JsonSerialize(using = ToStringSerializer.class) public class EmailAddressType implements Serializable { private static final long serialVersionUID = 1L; private String email; public EmailAddressType(String email) { this.email = email; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Override public String toString() { return email; } }
4) …against the following email structure pattern (represented by an EmailStructure interface):
public interface EmailStructure { static final String ATOM = "[a-z0-9!#$%&'*+/=?^_`{|}~-]"; static final String DOMAIN = "(" + ATOM + "+(\\." + ATOM + "+)+"; static final String IP_DOMAIN = "\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\]"; static final String PATTERN = "^" + ATOM + "+(\\." + ATOM + "+)*@" + DOMAIN + "|" + IP_DOMAIN + ")$"; }
5) finally this is how we will use the validation annotation in our code:
public class User { @ValidEmailSyntax private EmailAddressType email; ... //getters and setters ommited }
Happy coding! 🙂
Tagged: API, Java, REST, Validation
Leave a Reply