2015-12-01 14 views
5

hoconvalida Primavera per i parametri RequestBody legati alle collezioni in metodi controller

un'entità:

package org.ibp.soq; 

public class MyEntity { 

    private String field1; 
    private String field2; 

    //..getters and setters 

} 

Validator per l'entità:

package org.ibp.soq; 

import org.springframework.stereotype.Component; 
import org.springframework.validation.Errors; 
import org.springframework.validation.Validator; 

@Component 
public class MyEntityValidator implements Validator { 

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

    @Override 
    public void validate(Object target, Errors errors) { 
     MyEntity myEntity = (MyEntity) target; 
     // Logic to validate my entity 
     System.out.print(myEntity); 
    } 

} 

e

Il controller REST con metodo PUT di massa:

package org.ibp.soq; 

import java.util.List; 

import javax.validation.Valid; 

import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.web.bind.WebDataBinder; 
import org.springframework.web.bind.annotation.InitBinder; 
import org.springframework.web.bind.annotation.RequestBody; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RequestMethod; 
import org.springframework.web.bind.annotation.RestController; 

@RestController 
@RequestMapping("/myEntity") 
public class MyEntityRestResource { 

    @Autowired 
    private MyEntityValidator myEntityValidator; 

    @InitBinder 
    protected void initBinder(final WebDataBinder binder) { 
     binder.addValidators(this.myEntityValidator); 
    } 

    @RequestMapping(method = RequestMethod.PUT) 
    public void bulkCreate(@RequestBody @Valid List<MyEntity> myEntities) { 
     // Logic to bulk create entities here. 
     System.out.print(myEntities); 
    } 
} 

Quando faccio una richiesta PUT a questa risorsa con le seguenti corpo della richiesta:

[ 
    { 
    "field1": "AA", 
    "field2": "11" 
    }, 

    { 
    "field1": "BB", 
    "field2": "22" 
    } 
] 

L'errore che ottengo è:

"Invalid target for Validator [[email protected]b617e]: [[email protected], [email protected]]" 

Posso capire che questo è perché MyEntityValidator "supporta" la convalida singola MyEntity, non valida zione per ArrayList<MyEntity>.

MyEntityValidator funziona perfettamente se ho un singolo oggetto MyEntity nel corpo della richiesta e un metodo di controllo corrispondente con il parametro @RequestBody @Valid MyEntity myEntity.

Come è possibile estendere la configurazione del validatore che ho utilizzato per supportare la convalida della raccolta di MyEntity?

risposta

1

Come si può immaginare, questo non può essere ottenuto utilizzando Spring Validation. Spring Validation implementa Bean Validation (JSR 303/349) in contrapposizione alla convalida dell'oggetto. Sfortunatamente una collezione non è un bean Java. Hai due opzioni

  • Avvolgere l'elenco all'interno di una Java Bean
  • chiamata il validatore manualmente nella vostra massa creare metodo myEntityValidator. validate(targetObject, errors).
10

La soluzione è creare un personalizzato Validator per Collection e un @ControllerAdvice che registra che Validator nel WebDataBinders.

Validator:

import java.util.Collection; 

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

/** 
* Spring {@link Validator} that iterates over the elements of a 
* {@link Collection} and run the validation process for each of them 
* individually. 
* 
* @author DISID CORPORATION S.L. (www.disid.com) 
*/ 
public class CollectionValidator implements Validator { 

    private final Validator validator; 

    public CollectionValidator(LocalValidatorFactoryBean validatorFactory) { 
    this.validator = validatorFactory; 
    } 

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

    /** 
    * Validate each element inside the supplied {@link Collection}. 
    * 
    * The supplied errors instance is used to report the validation errors. 
    * 
    * @param target the collection that is to be validated 
    * @param errors contextual state about the validation process 
    */ 
    @Override 
    @SuppressWarnings("rawtypes") 
    public void validate(Object target, Errors errors) { 
    Collection collection = (Collection) target; 
    for (Object object : collection) { 
     ValidationUtils.invokeValidator(validator, object, errors); 
    } 
    } 
} 

ControllerAdvice:

import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; 
import org.springframework.web.bind.WebDataBinder; 
import org.springframework.web.bind.annotation.ControllerAdvice; 
import org.springframework.web.bind.annotation.InitBinder; 

/** 
* Controller advice that adds the {@link CollectionValidator} to the 
* {@link WebDataBinder}. 
* 
* @author DISID CORPORATION S.L. (www.disid.com) 
*/ 
@ControllerAdvice 
public class ValidatorAdvice { 

    @Autowired 
    protected LocalValidatorFactoryBean validator; 


    /** 
    * Adds the {@link CollectionValidator} to the supplied 
    * {@link WebDataBinder} 
    * 
    * @param binder web data binder. 
    */ 
    @InitBinder 
    public void initBinder(WebDataBinder binder) { 
    binder.addValidators(new CollectionValidator(validator)); 
    } 
} 
+0

Grazie! Certamente proverò questo approccio. –

+2

Utilizzare \ @ControllerAdvice applicherà CollectionValidator a tutti i controller. Il che causerà l'eccezione "java.lang.IllegalStateException: Target non valido per Validator" se si dispone di un'altra annotazione \ @Valid su oggetto non di raccolta. –

+0

Utilizzare invece \ @InitBinder ("attrName") o fare initBinder in un controller specifico. –

0

In realtà, questo può essere realizzato utilizzando Spring convalida e JSR303.

  • Esporre un bean MethodValidationPostProcessor.
  • Annota la classe controller con @Validated (org.springframework.validation.annotation.Validato)
  • Utilizzare le annotazioni di convalida JSR303 sui campi/metodi MyEntity.
  • Annota l'argomento RequestBody con @Valid (l'hai già fatto nell'esempio).
  • Aggiungere un metodo @ExceptionHandler per gestire MethodArgumentNotValidException. Questo può essere fatto nel controller o in una classe @ControllerAdvice.
Problemi correlati