2015-03-07 15 views
9

Załóżmy, że mam klasy podmiot próbka tak:Wiosna MVC - wyszukiwanie Walidatory automatycznie

public class Address { 
    ... 
} 

i odpowiedni walidator:

@Component 
public AddressValidator implements Validator { 

    @Override 
    public boolean supports(Class<?> entityClass) { 
     return entityClass.equals(Address.class); 
    } 

    @Override 
    public void validate(Object obj, Errors errors) { 
     ... 
    } 
} 

Gdy używam kontroler jak poniżej, wszystko działa:

@RestController 
@RequestMapping("/addresses") 
public class AddressController { 

    @Autowired 
    private AddressValidator validator; 

    @InitBinder 
    protected void initBinder(WebDataBinder binder) { 
     binder.setValidator(validator); 
    } 

    @RequestMapping(method=POST) 
    public Long addNewAddress(@Valid @RequestBody Address address) { 
     ... 
    } 
} 

Jednakże, jeśli pominąć walidator zarejestrowaniem część (czyli poniżej), walidacja nie jest t wykonane.

@Autowired 
private AddressValidator validator; 

@InitBinder 
protected void initBinder(WebDataBinder binder) { 
    binder.setValidator(validator); 
} 

Konieczność ręcznej rejestracji walidatorów wydaje się bezcelowa. Czy mogę nakazać Springowi automatyczne sprawdzenie poprawności (podobnie jak sprawdzane są kontrolery)?

Jest to aplikacja oparta na Spring Boot.

+0

Prosisz sposobu, aby automatycznie dodać 'Validator' kontekstu fasoli ramach walidacji Wiosna? – chrylis

+0

Po zaznaczeniu komponentu bean, takiego jak Adres, z adnotacją @Valid, chciałbym, aby Spring automatycznie sprawdził poprawny validator między komponentami bean implementującymi interfejs Validator. Czy jest taka funkcjonalność na wiosnę? Wydaje się łatwe do wdrożenia. W ten sposób nie musiałbym ręcznie ustawiać poprawnego weryfikatora w metodzie initBinder. –

+0

To jest interfejs Spring 'Validator', a nie' javax.validator.Validator'? Czy istnieje powód, dla którego nie używasz niestandardowego walidatora JSR-303, być może z niestandardową adnotacją '@ ValidAddress'? – chrylis

Odpowiedz

1

Można skonfigurować globalny Validator.

http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#validation-mvc

Jeśli używasz konfiguracji sprężyny Java z WebMvcConfigurationSupport można przesłonić getValidator()

/** 
* Override this method to provide a custom {@link Validator}. 
*/ 
protected Validator getValidator() { 
    return null; 
} 

Możesz zadzwonić setValidator (Validator) na globalnym WebBindingInitializer. Pozwala to skonfigurować instancję sprawdzania poprawności we wszystkich klasach @Controller. Można to łatwo osiągnąć za pomocą nazw Wiosna MVC:

<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:mvc="http://www.springframework.org/schema/mvc" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation=" 
     http://www.springframework.org/schema/beans 
     http://www.springframework.org/schema/beans/spring-beans.xsd 
     http://www.springframework.org/schema/mvc 
     http://www.springframework.org/schema/mvc/spring-mvc.xsd"> 

    <mvc:annotation-driven validator="globalValidator"/> 

</beans> 
+2

Możesz zarejestrować tylko jeden globalny weryfikator? W jaki sposób powiedziałbyś wiosce, aby załadowała wiele walidatorów i wybrała poprawną implementację do użycia, gdy parametr jest oznaczony przez @Valid? – Blacktiger

0

nie znalazłem kompilacji w Spring rozwiązanie tego problemu, ale tutaj jest to, co robię.

Oświadczam moje validatora fasolę w konfiguracji java wiosny tak:

@Bean 
public Validator studentValidator(){ 
    return new StudentValidator(); 
} 

@Bean 
public Validator carValidator(){ 
    return new CarValidator(); 
} 

Wtedy mam wszystkie kontrolery rozszerza kontroler bazowej tak:

public abstract class BaseController <T> { 
    public BaseController(List<Validator> validators) { 
     super(); 
     this.validators = validators; 
     } 
    //Register all validators 
    @InitBinder 
    protected void initBinder(WebDataBinder binder) { 
    validators.stream().forEach(v->binder.addValidators(v)); 
    } 


} 

Beton klasa tego kontrolera dostaje Lista wstrzyknięta za pomocą wstrzyknięcia zależności, tak:

@Controller 
public class StudentController extends BaseController<Student>{ 

    @Inject 
     public StudentController(List<Validator> validators) { 
     super(validators); 
     } 

} 

Podstawa e Kontroler używa metody oddzwaniania @InitBinder w celu zarejestrowania wszystkich walidatorów.

Jestem zaskoczony, że wiosna nie rejestruje automatycznie wszystkich obiektów w ścieżce klasy, które implementują interfejs Validator.

2

Możesz użyć mojego przykładu z gist lub poniżej. Chodzi o to, aby mieć główną funkcję CompositeValidator, która będzie posiadaczem wszystkich instancji Validator lub SmartValidator.

Obsługuje podpowiedzi i może być również zintegrowany z walidatorem adnotacji Hibernate (LocalValidatorFactoryBean). A także możliwe jest posiadanie więcej niż jednego walidatora dla konkretnego Modelu.


CompositeValidator.java

@Component 
public class CompositeValidator implements SmartValidator { 
    @Autowired 
    private List<Validator> validators = Collections.emptyList(); 

    @PostConstruct 
    public void init() { 
     Collections.sort(validators, AnnotationAwareOrderComparator.INSTANCE); 
    } 

    @Override 
    public boolean supports(Class<?> clazz) { 
     for (Validator validator : validators) { 
      if (validator.supports(clazz)) { 
       return true; 
      } 
     } 
     return false; 
    } 

    @Override 
    public void validate(Object target, Errors errors) { 
     validate(target, errors, javax.validation.groups.Default.class); 
    } 

    @Override 
    public void validate(Object target, Errors errors, Object... validationHints) { 
     Class<?> clazz = target.getClass(); 

     for (Validator validator : validators) { 
      if (validator.supports(clazz)) { 
       if (validator instanceof SmartValidator) { 
        ((SmartValidator) validator).validate(target, errors, validationHints); 
       } else { 
        validator.validate(target, errors); 
       } 
      } 
     } 
    } 
} 

SomeController.java

@Controller 
@RequestMapping("/my/resources") 
public class SomeController { 

    @RequestMapping(method = RequestMethod.POST) 
    public Object save(
      @Validated(javax.validation.groups.Default.class) // this interface descriptor (class) is used by default 
      @RequestBody MyResource myResource 
    ) { return null; } 
} 

Java Config

@Configuration 
    public class WebConfig { 
     /** used for Annotation based validation, it can be created by spring automaticaly and you don't do it manualy */ 
     // @Bean 
     // public Validator jsr303Validator() { 
     // LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); 
     // // validator.setValidationMessageSource(...); 
     // return validator; 
     // } 

     @Bean 
     public WebMvcConfigurerAdapter webMvcConfigurerAdapter() { 
      return new WebMvcConfigurerAdapter() { 
       @Autowired 
       private CompositeValidator validator; 

       @Override 
       public Validator getValidator() { 
        return validator; 
       } 
      } 
     } 

lub XML Config

<!-- used for Annotation based validation, it can be created by spring automaticaly and you don't do it manualy --> 
<!--<bean id="jsr303Validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">--> 
<!-- <property name="validationMessageSource" ref="messageSource"/>--> 
<!--</bean>--> 

<mvc:annotation-driven validator="compositeValidator"> 
    //... 
</mvc:annotation-driven>