Saltar al contenido

Spring MVC: ¿Cómo realizar la validación?

Investigamos en diferentes espacios para tenerte la solución para tu inquietud, si tienes dudas puedes dejarnos tu duda y te contestamos con gusto.

Solución:

Con Spring MVC, hay 3 formas diferentes de realizar la validación: usando anotación, manualmente o una combinación de ambas. No existe una única “manera más limpia y mejor” de validar, pero probablemente haya una que se adapte mejor a su proyecto / problema / contexto.

Tengamos un usuario:

public class User 

    private String name;

    ...


Método 1 : Si tiene Spring 3.x + y una validación simple para hacer, use javax.validation.constraints anotaciones (también conocidas como anotaciones JSR-303).

public class User 

    @NotNull
    private String name;

    ...


Necesitará un proveedor JSR-303 en sus bibliotecas, como Hibernate Validator, que es la implementación de referencia (esta biblioteca no tiene nada que ver con bases de datos y mapeo relacional, solo valida :-).

Luego, en tu controlador tendrías algo como:

@RequestMapping(value="/user", method=RequestMethod.POST)
public createUser(Model model, @Valid @ModelAttribute("user") User user, BindingResult result)
    if (result.hasErrors())
      // do something
    
    else 
      // do something else
    

Observe el @Valid: si el usuario tiene un null name, result.hasErrors () será true.

Método 2: Si tiene una validación compleja (como la lógica de validación de grandes empresas, la validación condicional en varios campos, etc.), o por alguna razón no puede usar el método 1, use la validación manual. Es una buena práctica separar el código del controlador de la lógica de validación. No cree su (s) clase (s) de validación desde cero, Spring proporciona una práctica org.springframework.validation.Validator interfaz (desde Spring 2).

Entonces digamos que tienes

public class User 

    private String name;

    private Integer birthYear;
    private User responsibleUser;
    ...


y desea realizar una validación “compleja” como: si la edad del usuario es menor de 18 años, el usuario responsable no debe ser null y la edad del usuario responsable debe ser mayor de 21 años.

Harás algo como esto

public class UserValidator implements Validator 

    @Override
    public boolean supports(Class clazz) 
      return User.class.equals(clazz);
    

    @Override
    public void validate(Object target, Errors errors) 
      User user = (User) target;

      if(user.getName() == null) 
          errors.rejectValue("name", "your_error_code");
      

      // do "complex" validation here

    


Entonces en tu controlador tendrías:

@RequestMapping(value="/user", method=RequestMethod.POST)
    public createUser(Model model, @ModelAttribute("user") User user, BindingResult result)
        UserValidator userValidator = new UserValidator();
        userValidator.validate(user, result);

        if (result.hasErrors())
          // do something
        
        else 
          // do something else
        

Si hay errores de validación, result.hasErrors () será true.

Nota: También puede configurar el validador en un método @InitBinder del controlador, con “binder.setValidator (…)” (en cuyo caso no sería posible un uso mixto del método 1 y 2, porque reemplaza el predeterminado validador). O puede crear una instancia en el constructor predeterminado del controlador. O tenga un @ Component / @ Service UserValidator que inyecte (@Autowired) en su controlador: muy útil, porque la mayoría de los validadores son singletons + la simulación de pruebas unitarias se vuelve más fácil + su validador podría llamar a otros componentes Spring.

Método 3:
¿Por qué no utilizar una combinación de ambos métodos? Validar las cosas simples, como el “nombre” attribute, con anotaciones (es rápido de hacer, conciso y más legible). Mantenga las validaciones pesadas para los validadores (cuando tomaría horas codificar anotaciones de validación complejas personalizadas, o simplemente cuando no es posible usar anotaciones). Hice esto en un proyecto anterior, funcionó como un encanto, rápido y fácil.

Advertencia : no debes equivocarte manejo de validación por manejo de excepciones. Lea esta publicación para saber cuándo usarlos.

Referencias:

  • Una publicación de blog muy interesante sobre la validación de frijoles (el enlace original está muerto)
  • Otra buena publicación de blog sobre validación (el enlace original está muerto)
  • La última documentación de Spring sobre validación

Hay dos formas de validar la entrada del usuario: anotaciones y heredando la clase Validator de Spring. Para casos simples, las anotaciones son agradables. Si necesita validaciones complejas (como la validación de campo cruzado, por ejemplo, el campo “verificar dirección de correo electrónico”), o si su modelo está validado en varios lugares de su aplicación con diferentes reglas, o si no tiene la capacidad de modificar su modelo de objeto colocando anotaciones en él, el Validador basado en herencia de Spring es el camino a seguir. Mostraré ejemplos de ambos.

La parte de validación real es la misma independientemente del tipo de validación que esté utilizando:

RequestMapping(value="fooPage", method = RequestMethod.POST)
public String processSubmit(@Valid @ModelAttribute("foo") Foo foo, BindingResult result, ModelMap m) 
    if(result.hasErrors()) 
        return "fooPage";
    
    ...
    return "successPage";

Si está utilizando anotaciones, su Foo la clase podría verse así:

public class Foo 

    @NotNull
    @Size(min = 1, max = 20)
    private String name;

    @NotNull
    @Min(1)
    @Max(110)
    private Integer age;

    // getters, setters

Las anotaciones anteriores son javax.validation.constraints anotaciones. También puede usar Hibernate’s
org.hibernate.validator.constraints, pero no parece que esté utilizando Hibernate.

Alternativamente, si implementa Spring’s Validator, crearía una clase de la siguiente manera:

public class FooValidator implements Validator 

    @Override
    public boolean supports(Class clazz) 
        return Foo.class.equals(clazz);
    

    @Override
    public void validate(Object target, Errors errors) 

        Foo foo = (Foo) target;

        if(foo.getName() == null) 
            errors.rejectValue("name", "name[emptyMessage]");
        
        else if(foo.getName().length() < 1 

Si usa el validador anterior, también debe vincular el validador al controlador Spring (no es necesario si usa anotaciones):

@InitBinder("foo")
protected void initBinder(WebDataBinder binder) 
    binder.setValidator(new FooValidator());

Consulte también los documentos de Spring.

Espero que ayude.

Me gustaría extender la buena respuesta de Jerome Dalbert. Encontré muy fácil escribir sus propios validadores de anotaciones en forma JSR-303. No está limitado a tener una validación de "un campo". Puede crear su propia anotación a nivel de tipo y tener una validación compleja (consulte los ejemplos a continuación). Prefiero esta forma porque no necesito mezclar diferentes tipos de validación (Spring y JSR-303) como lo hace Jerome. Además, estos validadores son "compatibles con Spring", por lo que puede usar @ Inject / @ Autowire de fábrica.

Ejemplo de validación de objetos personalizados:

@Target( TYPE, ANNOTATION_TYPE )
@Retention(RUNTIME)
@Constraint(validatedBy =  YourCustomObjectValidator.class )
public @interface YourCustomObjectValid 

    String message() default "YourCustomObjectValid.message";

    Class[] groups() default ;

    Class[] payload() default ;


public class YourCustomObjectValidator implements ConstraintValidator 

    @Override
    public void initialize(YourCustomObjectValid constraintAnnotation)  

    @Override
    public boolean isValid(YourCustomObject value, ConstraintValidatorContext context) 

        // Validate your complex logic 

        // Mark field with error
        ConstraintViolationBuilder cvb = context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate());
        cvb.addNode(someField).addConstraintViolation();

        return true;
    


@YourCustomObjectValid
public YourCustomObject 

Ejemplo de igualdad de campos genéricos:

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Target( TYPE, ANNOTATION_TYPE )
@Retention(RUNTIME)
@Constraint(validatedBy =  FieldsEqualityValidator.class )
public @interface FieldsEquality 

    String message() default "FieldsEquality.message";

    Class[] groups() default ;

    Class[] payload() default ;

    /**
     * Name of the first field that will be compared.
     * 
     * @return name
     */
    String firstFieldName();

    /**
     * Name of the second field that will be compared.
     * 
     * @return name
     */
    String secondFieldName();

    @Target( TYPE, ANNOTATION_TYPE )
    @Retention(RUNTIME)
    public @interface List 
        FieldsEquality[] value();
    





import java.lang.reflect.Field;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;

public class FieldsEqualityValidator implements ConstraintValidator 

    private static final Logger log = LoggerFactory.getLogger(FieldsEqualityValidator.class);

    private String firstFieldName;
    private String secondFieldName;

    @Override
    public void initialize(FieldsEquality constraintAnnotation) 
        firstFieldName = constraintAnnotation.firstFieldName();
        secondFieldName = constraintAnnotation.secondFieldName();
    

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) 
        if (value == null)
            return true;

        try 
            Class clazz = value.getClass();

            Field firstField = ReflectionUtils.findField(clazz, firstFieldName);
            firstField.setAccessible(true);
            Object first = firstField.get(value);

            Field secondField = ReflectionUtils.findField(clazz, secondFieldName);
            secondField.setAccessible(true);
            Object second = secondField.get(value);

            if (first != null && second != null && !first.equals(second)) 
                    ConstraintViolationBuilder cvb = context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate());
          cvb.addNode(firstFieldName).addConstraintViolation();

          ConstraintViolationBuilder cvb = context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate());
          cvb.addNode(someField).addConstraintViolation(secondFieldName);

                return false;
            
         catch (Exception e) 
            log.error("Cannot validate fileds equality in '" + value + "'!", e);
            return false;
        

        return true;
    


@FieldsEquality(firstFieldName = "password", secondFieldName = "confirmPassword")
public class NewUserForm 

    private String password;

    private String confirmPassword;


valoraciones y comentarios

Si te sientes suscitado, eres capaz de dejar una noticia acerca de qué te ha parecido este ensayo.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *