Buscar..


Introducción

Spring tiene soporte de validación de bean JSR303. Podemos usar esto para hacer validación de bean de entrada. Separe la lógica de validación de la lógica de negocios utilizando JSR303.

JSR303 Validación basada en anotaciones en ejemplos de resortes

Agregue cualquier implementación JSR 303 a su classpath. El popular utilizado es el validador Hibernate de Hibernate.

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>4.2.0.Final</version>
</dependency>

Digamos que hay un api de descanso para crear usuarios en el sistema

@RequestMapping(value="/registeruser", method=RequestMethod.POST)
public String registerUser(User user);

La muestra json de entrada se vería como se muestra a continuación

{"username" : "[email protected]", "password" : "password1", "password2":"password1"}

Usuario.java

public class User {

    private String username;
    private String password;
    private String password2;

    getXXX and setXXX

}

Podemos definir las validaciones JSR 303 en la clase de usuario como se muestra a continuación.

public class User {

    @NotEmpty
    @Size(min=5)
    @Email
    private String username;
    
    @NotEmpty
    private String password;
    
    @NotEmpty
    private String password2;

}

También es posible que debamos tener un validador de negocios, como la contraseña y la contraseña2 (confirmar contraseña) son las mismas, para esto podemos agregar un validador personalizado como se muestra a continuación. Escriba una anotación personalizada para anotar el campo de datos.

@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PasswordValidator.class)
public @interface GoodPassword {
    String message() default "Passwords wont match.";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

Escribe una clase de Validador para aplicar la lógica de Validación.

public class PastValidator implements ConstraintValidator<GoodPassword, User> {
    @Override
    public void initialize(GoodPassword annotation) {}
    
    @Override
    public boolean isValid(User user, ConstraintValidatorContext context) {
        return user.getPassword().equals(user.getPassword2());
    }
}

Agregando esta validación a la clase de usuario

@GoodPassword
public class User {

    @NotEmpty
    @Size(min=5)
    @Email
    private String username;
    
    @NotEmpty
    private String password;
    
    @NotEmpty
    private String password2;
}

@Valid activa la validación en Spring. BindingResult es un objeto inyectado por spring que tiene una lista de errores después de la validación.

public String registerUser(@Valid User user, BindingResult result);

La anotación JSR 303 tiene atributos de mensaje que se pueden usar para proporcionar mensajes personalizados.

@GoodPassword
public class User {

    @NotEmpty(message="Username Cant be empty")
    @Size(min=5, message="Username cant be les than 5 chars")
    @Email(message="Should be in email format")
    private String username;
    
    @NotEmpty(message="Password cant be empty")
    private String password;
    
    @NotEmpty(message="Password2 cant be empty")
    private String password2;

}

Spring JSR 303 Validation - Personaliza los mensajes de error

Supongamos que tenemos una clase simple con anotaciones de validación

public class UserDTO {
    @NotEmpty
    private String name;

    @Min(18)
    private int age;

//getters/setters
}

Un controlador para comprobar la validez de UserDTO.

@RestController
public class ValidationController {

    @RequestMapping(value = "/validate", method = RequestMethod.POST)
    public ResponseEntity<String> check(@Valid @RequestBody UserDTO userDTO,
           BindingResult bindingResult) {
        return new ResponseEntity<>("ok" , HttpStatus.OK);
    }
}

Y una prueba.

@Test
public void testValid() throws Exception {
    TestRestTemplate template = new TestRestTemplate();
    String url = base + contextPath + "/validate";
    Map<String, Object> params = new HashMap<>();
    params.put("name", "");
    params.put("age", "10");

    MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
    headers.add("Content-Type", "application/json");

    HttpEntity<Map<String, Object>> request = new HttpEntity<>(params, headers);
    String res = template.postForObject(url, request, String.class);

    assertThat(res, equalTo("ok"));
}

Tanto el nombre como la edad no son válidos, por lo que en BindingResult tenemos dos errores de validación. Cada uno tiene una matriz de códigos.

Códigos para el control mínimo

0 = "Min.userDTO.age"
1 = "Min.age"
2 = "Min.int"
3 = "Min"

Y para el cheque NotEmpty

0 = "NotEmpty.userDTO.name"
1 = "NotEmpty.name"
2 = "NotEmpty.java.lang.String"
3 = "NotEmpty"

Agreguemos un archivo custom.properties para sustituir los mensajes predeterminados.

@SpringBootApplication
@Configuration
public class DemoApplication {

    @Bean(name = "messageSource")
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource bean = new ReloadableResourceBundleMessageSource();
        bean.setBasename("classpath:custom");
        bean.setDefaultEncoding("UTF-8");
        return bean;
    }

    @Bean(name = "validator")
    public LocalValidatorFactoryBean validator() {
        LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
        bean.setValidationMessageSource(messageSource());
        return bean;
    }

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

Si agregamos al archivo custom.properties la línea

NotEmpty=The field must not be empty!

El nuevo valor se muestra para el error. Para resolver el validador de mensajes, revise los códigos que comienzan desde el principio para encontrar los mensajes adecuados.

Por lo tanto, cuando definimos la clave NotEmpty en el archivo .properties para todos los casos donde se @NotEmpty anotación @NotEmpty se aplica nuestro mensaje.

Si definimos un mensaje.

Min.int=Some custom message here.

Todas las anotaciones donde aplicamos min check a valores enteros utilizan el mensaje recién definido.

La misma lógica podría aplicarse si necesitamos localizar los mensajes de error de validación.

Uso de @Valid para validar POJOs anidados

Supongamos que tenemos un usuario de clase POJO que necesitamos validar.

public class User {

    @NotEmpty
    @Size(min=5)
    @Email
    private String email;
}

y un método de controlador para validar la instancia de usuario

public String registerUser(@Valid User user, BindingResult result);

Extendamos al usuario con una dirección POJO anidada que también necesitamos validar.

public class Address {

    @NotEmpty
    @Size(min=2, max=3)
    private String countryCode;
}

Solo agregue la anotación @Valid en el campo de dirección para ejecutar la validación de los POJOs anidados.

public class User {

    @NotEmpty
    @Size(min=5)
    @Email
    private String email;

    @Valid
    private Address address;
}


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow