Tag Archives: Validation

Custom Validation Annotations

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! 🙂

Advertisements