Cosa indica l'annotazione @Valid in primavera?


88

Nell'esempio seguente, il ScriptFileparametro è contrassegnato da @Validun'annotazione.

Cosa fa l' @Validannotazione?

@RequestMapping(value = "/scriptfile", method = RequestMethod.POST)    
public String create(@Valid ScriptFile scriptFile, BindingResult result, ModelMap modelMap) {    
    if (scriptFile == null) throw new IllegalArgumentException("A scriptFile is required");        
    if (result.hasErrors()) {        
        modelMap.addAttribute("scriptFile", scriptFile);            
        modelMap.addAttribute("showcases", ShowCase.findAllShowCases());            
        return "scriptfile/create";            
    }        
    scriptFile.persist();        
    return "redirect:/scriptfile/" + scriptFile.getId();        
}    

Risposte:


63

È a scopo di convalida.

Convalida È comune convalidare un modello dopo avervi associato l'input dell'utente. Spring 3 fornisce supporto per la convalida dichiarativa con JSR-303. Questo supporto è abilitato automaticamente se un provider JSR-303, come Hibernate Validator, è presente sul tuo classpath. Se abilitato, è possibile attivare la convalida semplicemente annotando un parametro del metodo Controller con l'annotazione @Valid: Dopo aver associato i parametri POST in arrivo, AppointmentForm verrà convalidato; in questo caso, per verificare che il valore del campo data non sia nullo e ricada in futuro.


Cerca qui per maggiori informazioni:
http://blog.springsource.com/2009/11/17/spring-3-type-conversion-and-validation/


37

Aggiungendo alle risposte precedenti, dai un'occhiata a quanto segue. AppointmentFormLa datecolonna di è annotata con un paio di annotazioni. Avendo @Validun'annotazione che attiva le convalide su AppointmentForm(in questo caso @NotNulle @Future). Queste annotazioni potrebbero provenire da diversi fornitori di JSR-303 (ad esempio, Hibernate, Spring..etc).

    @RequestMapping(value = "/appointments", method = RequestMethod.POST)
    public String add(@Valid AppointmentForm form, BindingResult result) {
        ....
    }

    static class AppointmentForm {

        @NotNull @Future
        private Date date;
    }

1
Ho un codice simile. Avevo rimosso @Validil ApplicationFormparametro ma, le convalide sono state ancora attivate sul campo date(impostato come null). Spiega per favore.
lupchiazoem

18

@Validdi per sé non ha nulla a che fare con la primavera. Fa parte delle specifiche di Bean Validation (ce ne sono diversi, l'ultimo è JSR 380 a partire dalla seconda metà del 2017), ma @Validè molto vecchio e deriva da JSR 303.

Come tutti sappiamo, Spring è molto bravo a fornire l'integrazione con tutti i diversi JSR e le librerie java in generale (si pensi a JPA, JTA, Caching, ecc.) E, naturalmente, anche quei ragazzi si sono occupati della convalida. Uno dei componenti chiave che facilita ciò è MethodValidationPostProcessor .

Cercare di rispondere alla tua domanda - @Validè molto utile per la cosiddetta convalida a cascata quando vuoi convalidare un grafico complesso e non solo gli elementi di primo livello di un oggetto. Ogni volta che vuoi andare più in profondità, devi usare @Valid. Questo è ciò che detta JSR. Spring si conformerà a questo con alcune piccole deviazioni (ad esempio ho provato a mettere il metodo RestController al @Validatedposto di @ValidRestController e la validazione funziona, ma lo stesso non si applica a un normale bean di "servizio").


Ma cosa convalida?
nipote

Per favore approfondisci, @nephewtom. Cosa vuoi chiarire?
yuranos

Ho letto JSR 303: Bean Validation, beanvalidation.org/1.0/spec ma ancora non ho capito su quale convalida verrà eseguita ScriptFile scriptFile.
nipote

ScriptFile al suo interno probabilmente ha un mucchio di campi e anche quei campi hanno annotazioni su di essi. È qui che entra in gioco la convalida. Sulla base della domanda originale non è chiaro cosa ci sia esattamente all'interno della classe ScriptFile.
yuranos

Ok grazie. Potresti mettere un esempio di cosa potrebbe convalidare se i suoi campi fossero una stringa, un numero intero e un fagiolo?
nipote

17

IIRC @Valid non è un'annotazione Spring ma un'annotazione JSR-303 (che è lo standard Bean Validation). Quello che fa è fondamentalmente controllare se i dati che invii al metodo sono validi o meno (convaliderà lo scriptFile per te).


2
Cosa significa "convaliderà lo scriptFile per te"? Verificare che non sia nullo, che abbia una sintassi o dei contenuti? In altre parole, cosa convalida e come? L'utente dovrebbe implementare qualcosa? Dove posso ottenere informazioni a riguardo? Grazie!
nipote

Per favore rispondi alla domanda di @ nephewtom, non è una risposta sufficiente per convalidare il punto principale mancante che è "contro cosa"?
monami

2
public String create(@Valid @NotNull ScriptFile scriptFile, BindingResult result, ModelMap modelMap) {    
    if (scriptFile == null) throw new IllegalArgumentException("A scriptFile is required");        

Immagino che questa @NotNullannotazione sia valida quindi se la condizione non è necessaria.


2

Penso di sapere dove sia diretta la tua domanda. E poiché questa domanda è quella che compare nei risultati principali di ricerca di Google, posso dare una risposta semplice su ciò che fa l'annotazione @Valid.

Presenterò 3 scenari su come ho usato @Valid

Modello:

public class Employee{
private String name;
@NotNull(message="cannot be null")
@Size(min=1, message="cannot be blank")
private String lastName;
 //Getters and Setters for both fields.
 //...
}

JSP:

...
<form:form action="processForm" modelAttribute="employee">
 <form:input type="text" path="name"/>
 <br>
 <form:input type="text" path="lastName"/>
<form:errors path="lastName"/>
<input type="submit" value="Submit"/>
</form:form>
...

Controller per lo scenario 1:

     @RequestMapping("processForm")
        public String processFormData(@Valid @ModelAttribute("employee") Employee employee){
        return "employee-confirmation-page";
    }

In questo scenario, dopo aver inviato il modulo con un campo lastName vuoto, riceverai una pagina di errore poiché stai applicando regole di convalida ma non lo stai gestendo in alcun modo.

Esempio di detto errore: pagina di eccezione

Controller per lo scenario 2:

 @RequestMapping("processForm")
    public String processFormData(@Valid @ModelAttribute("employee") Employee employee,
BindingResult bindingResult){
                return bindingResult.hasErrors() ? "employee-form" : "employee-confirmation-page";
            }

In questo scenario, stai passando tutti i risultati da quella convalida a bindingResult, quindi spetta a te decidere cosa fare con i risultati della convalida di quel modulo.

Controller per lo scenario 3:

@RequestMapping("processForm")
    public String processFormData(@Valid @ModelAttribute("employee") Employee employee){
                return "employee-confirmation-page";
            }
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> invalidFormProcessor(MethodArgumentNotValidException ex){
  //Your mapping of the errors...etc
}

In questo scenario non stai ancora gestendo gli errori come nel primo scenario, ma lo passi a un altro metodo che si prenderà cura dell'eccezione che @Valid attiva durante l'elaborazione del modello di modulo. Controlla questo vedere cosa fare con la mappatura e tutto il resto.

Per riassumere : @Valid da solo non fa altro che attivare la convalida dei campi annotati JSR 303 di convalida ( @NotNull, @Email, @Size, ecc ... ), è comunque necessario specificare una strategia di cosa fare con i risultati di detta validazione.

Spero di essere riuscito a chiarire qualcosa per le persone che potrebbero inciampare con questo.


1

Aggiungendo semplicemente alla risposta sopra, in un'applicazione web @validviene utilizzato in cui il bean da convalidare è anche annotato con annotazioni di convalida @NotNull, ad esempio @Email(annotazione di ibernazione), quindi quando si riceve l'input dall'utente i valori possono essere convalidati e il risultato dell'associazione avrà la convalida risultati. bindingResult.hasErrors()dirà se una qualsiasi convalida non è riuscita.


1

Un altro aspetto utile di @Valid non menzionato sopra è che (ad esempio: utilizzando Postman per testare un endpoint) @Valid formatterà l'output di una chiamata REST errata in JSON formattato invece di un blob di testo appena leggibile. Questo è molto utile se stai creando un'API consumabile commercialmente per i tuoi utenti.


1

Volevo aggiungere ulteriori dettagli su come il file @Valid funziona, soprattutto in primavera.

Tutto ciò che vorresti sapere sulla convalida in primavera è spiegato chiaramente e in dettaglio in https://reflectoring.io/bean-validation-with-spring-boot/ , ma copierò la risposta su come@Valid funziona incase il link scende.

L' @Validannotazione può essere aggiunta alle variabili in un metodo rest controller per convalidarle. Esistono 3 tipi di variabili che possono essere convalidate:

  • il corpo della richiesta,
  • variabili all'interno del percorso (ad esempio id in / foos / {id}) e,
  • parametri di query.

Quindi ora ... come si "convalida" la primavera? È possibile definire vincoli ai campi di una classe annotandoli con determinate annotazioni. Quindi, si passa un oggetto di quella classe a un Validator che controlla se i vincoli sono soddisfatti.

Ad esempio, supponiamo che avessi un metodo controller come questo:

@RestController
class ValidateRequestBodyController {

  @PostMapping("/validateBody")
  ResponseEntity<String> validateBody(@Valid @RequestBody Input input) {
    return ResponseEntity.ok("valid");
  }

}

Quindi questa è una richiesta POST che accetta un corpo di risposta e stiamo mappando quel corpo di risposta a una classe Input.

Ecco la classe Input:

class Input {

  @Min(1)
  @Max(10)
  private int numberBetweenOneAndTen;

  @Pattern(regexp = "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$")
  private String ipAddress;
  
  // ...
}

L'annotazione @Valid dirà a spring di andare a convalidare i dati passati al controller controllando che il numero intero numberBetweenOneAndTensia compreso tra 1 e 10 inclusi a causa di quelle annotazioni min e max. Verificherà anche che l'indirizzo IP passato corrisponda all'espressione regolare nell'annotazione.

nota a margine: l'espressione regolare non è perfetta .. potresti passare numeri a 3 cifre maggiori di 255 e corrisponderebbe comunque all'espressione regolare.


Ecco un esempio di convalida di una variabile di query e di una variabile di percorso:

@RestController
@Validated
class ValidateParametersController {

  @GetMapping("/validatePathVariable/{id}")
  ResponseEntity<String> validatePathVariable(
      @PathVariable("id") @Min(5) int id) {
    return ResponseEntity.ok("valid");
  }
  
  @GetMapping("/validateRequestParameter")
  ResponseEntity<String> validateRequestParameter(
      @RequestParam("param") @Min(5) int param) { 
    return ResponseEntity.ok("valid");
  }
}

In questo caso, poiché la variabile di query e la variabile di percorso sono solo numeri interi invece di classi complesse, mettiamo l'annotazione del vincolo @Min(5)direttamente sul parametro invece di usare @Valid.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.