Unterschiede in @Valid und @Validated Annotations im Frühjahr

1. Übersicht

In diesem kurzen Tutorial konzentrieren wir uns auf die Unterschiede zwischen @ Valid- und @ Validated- Annotationen im Frühjahr.

Das Überprüfen der Benutzereingaben ist in den meisten unserer Anwendungen eine gängige Funktion. Im Java-Ökosystem verwenden wir speziell die Java Standard Bean Validation API, um dies zu unterstützen. Darüber hinaus ist dies ab Version 4.0 auch gut in Spring integriert. Die Annotationen @Valid und @Validated stammen von dieser Standard Bean-API .

Schauen wir uns diese in den nächsten Abschnitten genauer an.

2. @Valid und @Validated Annotations

Im Frühjahr verwenden wir die @ Valid- Annotation von JSR-303 für die Validierung auf Methodenebene . Darüber hinaus verwenden wir es auch, um ein Mitgliedsattribut zur Validierung zu markieren . Diese Anmerkung unterstützt jedoch keine Gruppenüberprüfung.

Gruppen helfen dabei, die während der Validierung angewendeten Einschränkungen zu begrenzen. Ein besonderer Anwendungsfall sind UI-Assistenten. Hier können wir im ersten Schritt eine bestimmte Untergruppe von Feldern haben. Im folgenden Schritt kann es eine andere Gruppe geben, die zu derselben Bean gehört. Daher müssen wir in jedem Schritt Einschränkungen für diese begrenzten Felder anwenden, aber @Valid unterstützt dies nicht.

In diesem Fall müssen wir auf Gruppenebene Spring's @Validated verwenden, eine Variante des @Valid dieses JSR-303 . Dies wird auf Methodenebene verwendet. Zum Markieren von Mitgliedsattributen verwenden wir weiterhin die Annotation @Valid .

Lassen Sie uns nun gleich eintauchen und die Verwendung dieser Anmerkungen anhand eines Beispiels betrachten.

3. Beispiel

Betrachten wir ein einfaches Benutzerregistrierungsformular, das mit Spring Boot entwickelt wurde. Zunächst haben wir nur den Namen und die Passwortattribute :

public class UserAccount { @NotNull @Size(min = 4, max = 15) private String password; @NotBlank private String name; // standard constructors / setters / getters / toString } 

Schauen wir uns als nächstes den Controller an. Hier haben wir die saveBasicInfo- Methode mit der Annotation @Valid , um die Benutzereingabe zu validieren:

@RequestMapping(value = "/saveBasicInfo", method = RequestMethod.POST) public String saveBasicInfo( @Valid @ModelAttribute("useraccount") UserAccount useraccount, BindingResult result, ModelMap model) { if (result.hasErrors()) { return "error"; } return "success"; }

Testen wir nun diese Methode:

@Test public void givenSaveBasicInfo_whenCorrectInput_thenSuccess() throws Exception { this.mockMvc.perform(MockMvcRequestBuilders.post("/saveBasicInfo") .accept(MediaType.TEXT_HTML) .param("name", "test123") .param("password", "pass")) .andExpect(view().name("success")) .andExpect(status().isOk()) .andDo(print()); }

Nachdem Sie bestätigt haben, dass der Test erfolgreich ausgeführt wurde, erweitern wir nun die Funktionalität. Der nächste logische Schritt besteht darin, dies in ein mehrstufiges Registrierungsformular umzuwandeln, wie dies bei den meisten Assistenten der Fall ist. Der erste Schritt mit dem Namen und dem Passwort bleibt unverändert. Im zweiten Schritt rufen wir zusätzliche Informationen wie Alter und Telefon ab . Daher aktualisieren wir unser Domain-Objekt mit diesen zusätzlichen Feldern:

public class UserAccount { @NotNull @Size(min = 4, max = 15) private String password; @NotBlank private String name; @Min(value = 18, message = "Age should not be less than 18") private int age; @NotBlank private String phone; // standard constructors / setters / getters / toString } 

Diesmal werden wir jedoch feststellen, dass der vorherige Test fehlschlägt. Dies liegt daran, dass wir nicht in den Alters- und Telefonfeldern vorbeikommen , die auf der Benutzeroberfläche immer noch nicht im Bild enthalten sind . Um dieses Verhalten zu unterstützen, benötigen wir eine Gruppenüberprüfung und die Annotation @Validated .

Dazu müssen wir die Felder gruppieren und zwei unterschiedliche Gruppen erstellen. Zuerst müssen wir zwei Markierungsschnittstellen erstellen. Eine separate für jede Gruppe oder jeden Schritt. Die genaue Umsetzung finden Sie in unserem Artikel zur Gruppenvalidierung. Konzentrieren wir uns hier auf die Unterschiede in den Anmerkungen.

Wir haben die BasicInfo- Oberfläche für den ersten Schritt und die AdvanceInfo für den zweiten Schritt. Darüber hinaus aktualisieren wir unsere UserAccount- Klasse, um diese Markierungsschnittstellen wie folgt zu verwenden:

public class UserAccount { @NotNull(groups = BasicInfo.class) @Size(min = 4, max = 15, groups = BasicInfo.class) private String password; @NotBlank(groups = BasicInfo.class) private String name; @Min(value = 18, message = "Age should not be less than 18", groups = AdvanceInfo.class) private int age; @NotBlank(groups = AdvanceInfo.class) private String phone; // standard constructors / setters / getters / toString } 

Außerdem aktualisieren wir jetzt unseren Controller, um die Annotation @Validated anstelle von @Valid zu verwenden :

@RequestMapping(value = "/saveBasicInfoStep1", method = RequestMethod.POST) public String saveBasicInfoStep1( @Validated(BasicInfo.class) @ModelAttribute("useraccount") UserAccount useraccount, BindingResult result, ModelMap model) { if (result.hasErrors()) { return "error"; } return "success"; }

Aufgrund dieses Updates wird unser Test nun erfolgreich ausgeführt. Testen wir nun auch diese neue Methode:

@Test public void givenSaveBasicInfoStep1_whenCorrectInput_thenSuccess() throws Exception { this.mockMvc.perform(MockMvcRequestBuilders.post("/saveBasicInfoStep1") .accept(MediaType.TEXT_HTML) .param("name", "test123") .param("password", "pass")) .andExpect(view().name("success")) .andExpect(status().isOk()) .andDo(print()); }

Auch dies läuft erfolgreich. Daher können wir sehen, wie wichtig die Verwendung von @Validated für die Gruppenvalidierung ist.

Als nächstes wollen wir sehen, wie wichtig @Valid ist, um die Validierung verschachtelter Attribute auszulösen.

4. Markieren Sie verschachtelte Objekte mit @Valid Annotation

Die Annotation @Valid wird insbesondere zum Markieren verschachtelter Attribute verwendet . Dies löst die Validierung des verschachtelten Objekts aus. In unserem aktuellen Szenario erstellen wir beispielsweise ein UserAddress- Objekt:

public class UserAddress { @NotBlank private String countryCode; // standard constructors / setters / getters / toString }

Um die Validierung dieses verschachtelten Objekts sicherzustellen, wird das Attribut mit der Annotation @Valid verziert :

public class UserAccount { //... @Valid @NotNull(groups = AdvanceInfo.class) private UserAddress useraddress; // standard constructors / setters / getters / toString }

5. Vor- und Nachteile

Schauen wir uns einige Vor- und Nachteile der Verwendung von @ Valid- und @ Validated- Annotationen im Frühjahr an.

Die Annotation @Valid stellt die Validierung des gesamten Objekts sicher. Wichtig ist, dass die Validierung der gesamten Objektdiagramme durchgeführt wird. Dies führt jedoch zu Problemen für Szenarien, die nur teilweise validiert werden müssen.

Auf der anderen Seite können wir @Validated für die Gruppenvalidierung verwenden, einschließlich der obigen Teilvalidierung . In diesem Fall müssen die validierten Entitäten jedoch die Validierungsregeln für alle Gruppen oder Anwendungsfälle kennen, in denen sie verwendet werden. Hier mischen wir Bedenken, daher kann dies zu einem Anti-Muster führen.

6. Fazit

In diesem kurzen Tutorial haben wir die wichtigsten Unterschiede zwischen @Valid und @Validated Annotations untersucht.

Abschließend verwenden wir für jede grundlegende Validierung die Annotation JSR @Valid in unseren Methodenaufrufen. Auf der anderen Seite müssen wir für jede Gruppenvalidierung, einschließlich Gruppensequenzen, die @ Validated- Annotation von Spring in unserem Methodenaufruf verwenden. Die Annotation @Valid wird auch benötigt, um die Validierung verschachtelter Eigenschaften auszulösen.

Wie immer ist der in diesem Artikel vorgestellte Code auf GitHub verfügbar.