Agrupando restrições de validação Javax
1. Introdução
Em nosso tutorialJava Bean Validation Basics, vimos o uso de várias restriçõesjavax.validation integradas. Neste tutorial, veremoshow to group javax.validation constraints.
2. Caso de Uso
Existem muitos cenários em que precisamosapply constraints on a certain set of fields of the bean, and then later we want to apply constraints on another set of fields of the same bean.
Por exemplo, vamos imaginar que temos um formulário de inscrição em duas etapas. Na primeira etapa, solicitamos que o usuário forneça informações básicas, como nome, sobrenome, identificação de email, número de telefone e captcha. Quando o usuário envia esses dados, queremos validar apenas essas informações.
Na próxima etapa, solicitamos ao usuário que forneça outras informações, como um endereço, e também queremos validar essas informações - observe que o captcha está presente nas duas etapas.
3. Restrições de validação de agrupamento
Todas as restrições de validaçãojavax têm um atributo chamadogroups.When we add a constraint to an element, we can declare the name of the group to which the constraint belongs. Isso é feito especificando o nome da classe da interface do grupo nos atributosgroups da restrição.
A melhor maneira de entender alguma coisa é sujar as mãos. Vamos ver em ação como combinamos as restriçõesjavax em grupos.
3.1. Declarando grupos de restrições
O primeiro passo é criar algumas interfaces. Essas interfaces serão os nomes dos grupos de restrições. Em nosso caso de uso, estamos dividindo as restrições de validação em dois grupos.
Vejamos o primeiro grupo de restrição,BasicInfo:
public interface BasicInfo {
}
O próximo grupo de restrição éAdvanceInfo:
public interface AdvanceInfo {
}
3.2. Usando grupos de restrições
Agora que declaramos nossos grupos de restrição, é hora de usá-los em nosso Java beanRegistrationForm:
public class RegistrationForm {
@NotBlank(groups = BasicInfo.class)
private String firstName;
@NotBlank(groups = BasicInfo.class)
private String lastName;
@Email(groups = BasicInfo.class)
private String email;
@NotBlank(groups = BasicInfo.class)
private String phone;
@NotBlank(groups = {BasicInfo.class, AdvanceInfo.class})
private String captcha;
@NotBlank(groups = AdvanceInfo.class)
private String street;
@NotBlank(groups = AdvanceInfo.class)
private String houseNumber;
@NotBlank(groups = AdvanceInfo.class)
private String zipCode;
@NotBlank(groups = AdvanceInfo.class)
private String city;
@NotBlank(groups = AdvanceInfo.class)
private String contry;
}
Com o atributo constraintgroups, dividimos os campos de nosso bean em dois grupos de acordo com nosso caso de uso. By default, all constraints are included in the Default constraint group.
3.3. Testando restrições com um grupo
Agora que declaramos os grupos de restrição e os usamos em nossa classe de bean, é hora de ver esses grupos de restrição em ação.
Primeiro, veremos quando as informações básicas não estão completas, usando nosso grupo de restriçãoBasicInfo para validação. Devemos obter uma violação de restrição para qualquer campo deixado em branco onde usamosBasicInfo.class no atributogroups da restrição@NotBlank do campo:
public class RegistrationFormUnitTest {
private static Validator validator;
@BeforeClass
public static void setupValidatorInstance() {
validator = Validation.buildDefaultValidatorFactory().getValidator();
}
@Test
public void whenBasicInfoIsNotComplete_thenShouldGiveConstraintViolationsOnlyForBasicInfo() {
RegistrationForm form = buildRegistrationFormWithBasicInfo();
form.setFirstName("");
Set> violations = validator.validate(form, BasicInfo.class);
assertThat(violations.size()).isEqualTo(1);
violations.forEach(action -> {
assertThat(action.getMessage()).isEqualTo("must not be blank");
assertThat(action.getPropertyPath().toString()).isEqualTo("firstName");
});
}
private RegistrationForm buildRegistrationFormWithBasicInfo() {
RegistrationForm form = new RegistrationForm();
form.setFirstName("devender");
form.setLastName("kumar");
form.setEmail("[email protected]");
form.setPhone("12345");
form.setCaptcha("Y2HAhU5T");
return form;
}
//... additional tests
}
No próximo cenário, vamos verificar quando as informações avançadas estão incompletas, usando nosso grupo de restriçãoAdvanceInfo para validação:
@Test
public void whenAdvanceInfoIsNotComplete_thenShouldGiveConstraintViolationsOnlyForAdvanceInfo() {
RegistrationForm form = buildRegistrationFormWithAdvanceInfo();
form.setZipCode("");
Set> violations = validator.validate(form, AdvanceInfo.class);
assertThat(violations.size()).isEqualTo(1);
violations.forEach(action -> {
assertThat(action.getMessage()).isEqualTo("must not be blank");
assertThat(action.getPropertyPath().toString()).isEqualTo("zipCode");
});
}
private RegistrationForm buildRegistrationFormWithAdvanceInfo() {
RegistrationForm form = new RegistrationForm();
return populateAdvanceInfo(form);
}
private RegistrationForm populateAdvanceInfo(RegistrationForm form) {
form.setCity("Berlin");
form.setContry("DE");
form.setStreet("alexa str.");
form.setZipCode("19923");
form.setHouseNumber("2a");
form.setCaptcha("Y2HAhU5T");
return form;
}
3.4. Testando restrições com vários grupos
Podemos especificar vários grupos para uma restrição. Em nosso caso de uso, estamos usandocaptcha nas informações básicas e avançadas. Vamos primeiro testar ocaptcha comBasicInfo:
@Test
public void whenCaptchaIsBlank_thenShouldGiveConstraintViolationsForBasicInfo() {
RegistrationForm form = buildRegistrationFormWithBasicInfo();
form.setCaptcha("");
Set> violations = validator.validate(form, BasicInfo.class);
assertThat(violations.size()).isEqualTo(1);
violations.forEach(action -> {
assertThat(action.getMessage()).isEqualTo("must not be blank");
assertThat(action.getPropertyPath().toString()).isEqualTo("captcha");
});
}
Agora vamos testar ocaptcha comAdvanceInfo:
@Test
public void whenCaptchaIsBlank_thenShouldGiveConstraintViolationsForAdvanceInfo() {
RegistrationForm form = buildRegistrationFormWithAdvanceInfo();
form.setCaptcha("");
Set> violations = validator.validate(form, AdvanceInfo.class);
assertThat(violations.size()).isEqualTo(1);
violations.forEach(action -> {
assertThat(action.getMessage()).isEqualTo("must not be blank");
assertThat(action.getPropertyPath().toString()).isEqualTo("captcha");
});
}
4. Especificando a ordem de validação do grupo de restrição comGroupSequence
Por padrão, os grupos de restrições não são avaliados em nenhuma ordem específica. Mas podemos ter casos de uso em que alguns grupos devem ser validados antes de outros. Para conseguir isso, podemosspecify the order of group validation using GroupSequence.
Existem duas maneiras de usar a anotaçãoGroupSequence:
-
na entidade que está sendo validada
-
em umInterface
4.1. UsandoGroupSequence na entidade sendo validada
Essa é uma maneira simples de ordenar as restrições. Vamos anotar a entidade comGroupSequencee especificar a ordem das restrições:
@GroupSequence({BasicInfo.class, AdvanceInfo.class})
public class RegistrationForm {
@NotBlank(groups = BasicInfo.class)
private String firstName;
@NotBlank(groups = AdvanceInfo.class)
private String street;
}
4.2. UsandoGroupSequence em uma interface
Também podemos especificar a ordem de validação da restrição usando uminterface. A vantagem dessa abordagem é que a mesma sequência pode ser usada para outras entidades. Vamos ver como podemos usarGroupSequence com as interfaces que definimos acima:
@GroupSequence({BasicInfo.class, AdvanceInfo.class})
public interface CompleteInfo {
}
4.3. TestandoGroupSequence
Agora vamos testarGroupSequence. Primeiro, vamos testar que seBasicInfo estiver incompleto, a restrição de grupoAdvanceInfo não será avaliada:
@Test
public void whenBasicInfoIsNotComplete_thenShouldGiveConstraintViolationsForBasicInfoOnly() {
RegistrationForm form = buildRegistrationFormWithBasicInfo();
form.setFirstName("");
Set> violations = validator.validate(form, CompleteInfo.class);
assertThat(violations.size()).isEqualTo(1);
violations.forEach(action -> {
assertThat(action.getMessage()).isEqualTo("must not be blank");
assertThat(action.getPropertyPath().toString()).isEqualTo("firstName");
});
}
Em seguida, teste que, quandoBasicInfo for concluído, a restriçãoAdvanceInfo deve ser avaliada:
@Test
public void whenBasicAndAdvanceInfoIsComplete_thenShouldNotGiveConstraintViolationsWithCompleteInfoValidationGroup() {
RegistrationForm form = buildRegistrationFormWithBasicAndAdvanceInfo();
Set> violations = validator.validate(form, CompleteInfo.class);
assertThat(violations.size()).isEqualTo(0);
}
5. Conclusão
Neste tutorial rápido, vimos como agrupar restriçõesjavax.validation.
Como de costume, todos os trechos de código estão disponíveisover on GitHub.