Use Spring's Validator para la validación

Use Spring's Validator para la validación

Verificación de un solo objeto

Consideremos un pequeño objeto de datos:

import lombok.Data;

@Data
public class Person {

    private String name;
    private int age;
}

Implementaremos los dos métodos siguientes para proporcionar el comportamiento de validación del
método de interfaz de clase de persona org.springframework.validation.Validator:

  • soporte (Clase): ¿Puede este procedimiento de verificación verificar la instancia de la Clase proporcionada?
  • validar (Objeto, org.springframework.validation.Errors): validar el objeto dado y registrarse con el objeto Errores dado en caso de error de validación

Implementar Validator es muy simple, especialmente si conoce la clase de ayuda ValidationUtils que también proporciona Spring Framework.

import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

/**
 * @author Created by niugang on 2020/3/30/16:45
 */
public class PersonValidator implements Validator {

    /**
     * This Validator validates *just* Person instances
     */
    @Override
    public boolean supports(Class<?> clazz) {
        return Person.class.equals(clazz);
    }

    @Override
    public void validate(Object obj, Errors e) {
        ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
        Person p = (Person) obj;
        if (p.getAge() < 0) {
            e.rejectValue("age", "negative value","年龄不能为负值");
        } else if (p.getAge() > 110) {
            e.rejectValue("age", "too old","年龄不能超过110");
        }
    }

}

Como puede ver, el método staticInoe rechazado (..) en la clase ValidationUtils se usa para rechazar el atributo "nombre" (si el atributo es nulo o una cadena vacía). Eche un vistazo a los javadocs ValidationUtils para ver qué funciones proporciona además del ejemplo mostrado anteriormente.

verificación del controlador

    @PostMapping(value = "persons")
    public void savePerson(@RequestBody Person person) {
        log.info("请求参数:{}", person);


//        PersonValidator validator = new PersonValidator();
//        if(validator.supports(Person.class)){
//            BindException errors = new BindException(person, "person");
//            validator.validate(person,errors);
//            List<ObjectError> allErrors = errors.getAllErrors();
//            log.info("size="+allErrors.size());
//            for (int i=0;i<allErrors.size();i++) {
//               log.info(allErrors.get(i).getCode());
//            }
//
//        }

        DataBinder binder = new DataBinder(person);
        binder.setValidator(new PersonValidator());
       // validate the target object
        binder.validate();
      // get BindingResult that includes any validation errors
        BindingResult results = binder.getBindingResult();
        log.info("results:{}", results);
    }
Verificación de objetos anidados

Aunque se puede implementar una sola clase Validator para validar cada objeto anidado en el objeto rico, es mejor encapsular el
objeto lógico de validación de cada clase anidada en su propia implementación Validator. Un ejemplo simple de un objeto "rico" es un Cliente que consta de dos propiedades de Cadena (el nombre y el segundo nombre) y un objeto de Dirección complejo. Los objetos de dirección se pueden usar independientemente de los objetos del cliente, por lo que se ha implementado un AddressValidator único. Si desea que CustomerValidator reutilice la lógica contenida en la clase AddressValidator sin copiar y pegar, puede inyectar dependencia o instanciar un AddressValidator en su CustomerValidator y usarlo de la siguiente manera:

import lombok.Data;

/**
 * @author Created by niugang on 2020/3/30/18:42
 */
@Data
public class Address {

    private String  location;
}

import lombok.Data;

/**
 * @author Created by niugang on 2020/3/30/18:40
 */
@Data
public class Customer {

    private String firstName;
    private String surname;

    private Address address;
}

import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

/**
 * @author Created by niugang on 2020/3/30/18:44
 */
public class AddressValidator implements Validator {

    /**
     * This Validator validates *just* Address instances
     */
    @Override
    public boolean supports(Class<?> clazz) {
        return Address.class.equals(clazz);
    }

    @Override
    public void validate(Object obj, Errors e) {
        ValidationUtils.rejectIfEmpty(e, "location", "location.empty");
        Address p = (Address) obj;
        if (p != null && p.getLocation() != null && p.getLocation().length() > 5) {
            e.rejectValue("location", "value too length", "长度不能超过5");
        }
    }
}
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

/**
 * @author Created by niugang on 2020/3/30/18:47
 */
public class CustomerValidator implements Validator {
    private final Validator addressValidator;

    public CustomerValidator(Validator addressValidator) {

        if (addressValidator == null) {
            throw new IllegalArgumentException("The supplied [Validator] is " +
                    "required and must not be null.");
        }
        if (!addressValidator.supports(Address.class)) {
            throw new IllegalArgumentException("The supplied [Validator] must " +
                    "support the validation of [Address] instances.");
        }
        this.addressValidator = addressValidator;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return Customer.class.isAssignableFrom(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "field.required");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "surname", "field.required");
        Customer customer = (Customer) target;
        try {
            errors.pushNestedPath("address");
            ValidationUtils.invokeValidator(this.addressValidator, customer.getAddress(), errors);
        } finally {
            errors.popNestedPath();
        }
    }
}

Analiza el código en un mensaje de error

Ya hemos discutido el enlace de datos y la validación. Enviar el mensaje correspondiente al error de verificación es lo último que debemos discutir. En el ejemplo que se muestra arriba, rechazamos los campos de nombre y edad. Si queremos usar MessageSource para generar un mensaje de error, usaremos el código de error ("nombre" y "edad" en este caso) dado al rechazar el campo. Cuando llama (directa o indirectamente, como la clase ValidationUtils) rechazarValor o uno de los otros métodos de rechazo desde la interfaz Errores, la implementación subyacente registrará no solo el código que ingresa, sino también muchos otros códigos de error. Los códigos de error que registra están determinados por el MessageCodesResolver utilizado. De manera predeterminada, se utiliza DefaultMessageCodesResolver, por ejemplo, no solo registra el mensaje con el código que proporciona, sino que también incluye el mensaje que contiene el nombre del campo que pasa al método de rechazo. Por lo tanto, si usa rechazarValor ("edad", "too.darn.old") para rechazar el campo, además del código too.darn.old, Spring también registrará too.darn.old.age y too.darn.old.age .int (por lo que el primero contendrá el nombre del campo y el segundo contendrá el tipo de campo); esto se hace para facilitar a los desarrolladores ayudarlos a localizar mensajes de error, etc.

Validador de primavera

Spring 3 ha realizado algunas mejoras en su soporte de verificación. Primero, la API de autenticación JSR-303 Bean ahora es totalmente compatible. En segundo lugar, cuando se usa mediante programación, el DataBinder de Spring ahora puede validar objetos y vincularlos. En tercer lugar, Spring MVC ahora admite la verificación declarativa @Controller input.

Descripción general de la API de autenticación de frijol JSR-303

JSR-303 estandariza la declaración de restricción de verificación y los metadatos de la plataforma Java. Con esta API, puede aplicar restricciones de validación declarativa y atributos de modelo de dominio de anotación en tiempo de ejecución. Puede aprovechar muchas restricciones incorporadas. También puede definir sus propias restricciones personalizadas.

Objeto individual

Para ilustrar esto, considere un modelo PersonForm simple con dos propiedades:

public class PersonForm {
private String name;
private int age;
}

JSR-303 le permite definir restricciones de verificación declarativas para los siguientes atributos:

public class PersonForm {
@NotNull
@Size(max=64)
private String name;
@Min(0)
private int age;
}
 @PostMapping(value = "personforms.do")
 public void saveCustomer(@RequestBody @Validated PersonForm personForm) {
     log.info("请求参数:{}", personForm);
 }

Cheque en cascada

La API de validación de Bean no solo permite validar una sola instancia de clase, sino que también puede completar un gráfico de objeto (validación en cascada). Para hacer esto, simplemente comente para indicar uno,
como se muestra en la validación en cascada, use @Valid para referirse a otro objeto.

import lombok.Data;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;

/**
 * @author Created by niugang on 2020/3/30/19:42
 */
@Data
public class Car {

    @NotNull
    private String type;

    @NotNull
    @Valid
    private PersonInfo driver;
}

import lombok.Data;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

/**
 * @author Created by niugang on 2020/3/30/19:42
 */
@Data
public class PersonInfo {

    @NotNull
    @Size(min = 5)
    private String name;
}


Conjunto de verificación en cascada

import lombok.Data;
import org.hibernate.validator.constraints.NotBlank;

import javax.validation.constraints.Min;

/**
 * @author Created by niugang on 2020/4/2/11:32
 */
@Data
public class Dog {


    @NotBlank
    private  String  name;

    @NotBlank
    private  String type;

    @Min(0)
    private int age;
}

import javax.validation.Valid;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Created by niugang on 2020/4/2/11:31
 */
@Data
public class Animal {


    @Min(1)
    private int count;


    @Valid
    @NotNull
    @Size(min = 1)
    private List<Dog> dogs= new ArrayList<>();

}

Chequeo grupal

La clase Driver en Driver extiende Persona y agrega atributos age y hasDrivingLicense. El conductor debe tener al menos 18 años (@Min (18)) y tener una licencia de conducir (@AssertTrue). Las dos restricciones definidas en estos atributos pertenecen al grupo DriverChecks es solo una interfaz de marcado simple.

import lombok.Data;

import javax.validation.constraints.NotNull;

/**
 * @author Created by niugang on 2020/4/2/14:44
 */
@Data
public class Person {
    @NotNull
    private String name;
}

import lombok.Data;

import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.Min;

/**
 * @author Created by niugang on 2020/4/2/14:45
 */
@Data
public class Driver extends Person {
    @Min(
            value = 18,
            message = "You have to be 18 to drive a car",
            groups = DriverChecks.class
    )
    public int age;
    @AssertTrue(
            message = "You first have to pass the driving test",
            groups = DriverChecks.class
    )
    public boolean hasDrivingLicense;
}

/**
 * @author Created by niugang on 2020/4/2/14:45
 */
public interface DriverChecks {
}

controlador

La siguiente solicitud solo verificará los atributos marcados como DriverChecks

  @PostMapping(value = "drivers.do")
    public void cars(@RequestBody @Validated(DriverChecks.class) Driver driver, BindingResult result) {
        log.info("请求参数:{}", driver);
        log.info("请求参数 result:{}", result);
    }

Configurar el proveedor de autenticación de frijol

Spring proporciona soporte completo para la API de validación de Bean. Esto incluye soporte conveniente para guiar a los proveedores de validación de JSR-303 / JSR-349 Bean como Spring Beans. Esto permite insertar javax.validation.ValidatorFactory o javax.validation.Validator en cualquier lugar de la aplicación donde se requiera validación.

Use LocalValidatorFactoryBean para configurar el validador predeterminado como Spring Bean:

<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>

La configuración básica anterior activará la verificación de Bean para inicializar utilizando su mecanismo de arranque predeterminado. Se espera que los proveedores JSR-303 / JSR-349 como Hibernate Validator aparezcan en el classpath y detecten automáticamente al proveedor.

Validador de inyección

LocalValidatorFactoryBean implementa javax.validation.ValidatorFactory y javax.validation.Validator, así como el
org.springframework.validation.Validator de Spring . Puede inyectar una referencia a una de estas dos interfaces en un bean que necesita llamar a la lógica de validación.

Si desea utilizar Bean Validation, inyecte la API de referencia a javax.validation.Validator directamente:

import javax.validation.Validator;
@Service
public class MyService {
@Autowired
private Validator validator;

Si su bean requiere la API Spring Validation, inyecte una referencia a org.springframework.validation.Validator:

@RestController
@RequestMapping("/validator/")
@Slf4j
public class TestValidatorController{
    
    private final Validator validator;

    @Autowired
    public TestValidatorController(Validator validator) {
        this.validator = validator;
    }
    
      @PostMapping(value = "v2/drivers.do")
      public void carss(@RequestBody Driver driver) {
        BindException bindException = new BindException(driver, "driver");
        validator.validate(driver,bindException);
        log.info("请求参数:{}", driver);
        log.info("请求参数 result:{}", bindException.getBindingResult());
    }

}
@Data
public class Driver {

    @NotNull
    private String name;
    @Min(
            value = 18,
            message = "You have to be 18 to drive a car"
    )

    public int age;

    @AssertTrue(
            message = "You first have to pass the driving test"
    )

    public boolean hasDrivingLicense;

}

Configurar restricciones personalizadas

Cada restricción de validación de bean consta de dos partes. Primero, la anotación @Constraint declara restricciones y sus propiedades configurables. En segundo lugar, implemente la interfaz javax.validation.ConstraintValidator que implementa el comportamiento restringido. Para asociar la declaración con la implementación, cada anotación @Constraint hace referencia a una clase de implementación ValidationConstraint correspondiente. En tiempo de ejecución, cuando se encuentran anotaciones de restricción en el modelo de dominio, ConstraintValidatorFactory crea una instancia de la implementación a la que se hace referencia.

De manera predeterminada, LocalValidatorFactoryBean configura un SpringConstraintValidatorFactory que usa Spring para crear una instancia de ConstraintValidator. Esto permite que sus Validadores de restricción personalizados se beneficien de la inyección de dependencia como cualquier otro Spring Bean

A continuación se muestra un ejemplo de una declaración personalizada @Constraint, seguida de una implementación de ConstraintValidator asociada que usa Spring para la inyección de dependencia:

@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy=MyConstraintValidator.class)
public @interface MyConstraint {
}
import javax.validation.ConstraintValidator;
public class MyConstraintValidator implements ConstraintValidator {
@Autowired;
private Foo aDependency;
...
}

Como puede ver, la implementación de ConstraintValidator puede tener una dependencia de @Autowired como cualquier otro bean Spring.

Verificación del método basado en resorte

Eso es escribir un comentario sobre el método para verificar los parámetros

La función de validación de métodos compatible con Bean Validation 1.1 y la extensión personalizada de Hibernate Validator 4.3 también se pueden integrar en el contexto de Spring de la siguiente manera: Definición de bean MethodValidationPostProcessor:

<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>

Para ser elegible para la validación del método impulsado por Spring, todas las clases de destino deben ser anotadas con la anotación @Validated de Spring, y opcionalmente pueden declarar el grupo de validación para usar. Eche un vistazo a los detalles de configuración de los proveedores de Hibernate Validator y Bean Validation 1.1 utilizando MethodValidationPostProcessor javadocs

Configurar un DataBinder

A partir de Spring 3, puede usar Validator para configurar instancias de DataBinder. Después de la configuración, puede llamar a Validator llamando a binder.validate (). Cualquier error de verificación se agrega automáticamente a BindingResult.
Cuando se usa DataBinder mediante programación, se puede usar para llamar a la lógica de verificación después de vincular al objeto de destino:

Foo target = new Foo();
DataBinder binder = new DataBinder(target);
binder.setValidator(new FooValidator());
// bind to the target object
binder.bind(propertyValues);
// validate the target object
binder.validate();
// get BindingResult that includes any validation errors
BindingResult results = binder.getBindingResult();

Inserte la descripción de la imagen aquí

Supongo que te gusta

Origin www.cnblogs.com/niugang0920/p/12689224.html
Recomendado
Clasificación