Spring MVC Custom Validation

1. Oversigt

Når vi har brug for at validere brugerinput, tilbyder Spring MVC generelt standarddefinerede validatorer.

Men når vi skal validere en mere bestemt type input, Vi har muligheden for at oprette vores egen brugerdefinerede valideringslogik.

I denne artikel gør vi netop det - vi opretter en brugerdefineret validator til validering af en formular med et telefonnummerfelt og viser derefter en brugerdefineret validator til flere felter.

Denne artikel fokuserer på Spring MVC. Vores artikel Validering i Spring Boot beskriver, hvordan du foretager brugerdefinerede valideringer i Spring Boot.

2. Opsætning

For at få gavn af API'et skal du føje afhængigheden til din pom.xml fil:

 org.hibernate hibernate-validator 6.0.10.Final 

Den seneste version af afhængigheden kan kontrolleres her.

Hvis vi bruger Spring Boot, kan vi kun tilføje spring-boot-starter-web, som vil bringe i dvale-validator afhængighed også.

3. Brugerdefineret validering

Oprettelse af en brugerdefineret validator indebærer, at vi ruller vores egen kommentar ud og bruger den i vores model til at håndhæve valideringsreglerne.

Så lad os oprette vores brugerdefineret validator - som kontrollerer telefonnumre. Telefonnummeret skal være et nummer med mere end otte cifre, men ikke mere end 11 cifre.

4. Den nye kommentar

Lad os oprette et nyt @interface for at definere vores kommentar:

@Documented @Constraint (validatedBy = ContactNumberValidator.class) @Target ({ElementType.METHOD, ElementType.FIELD}) @Retention (RetentionPolicy.RUNTIME) public @interface ContactNumberConstraint {Strengmeddelelse () standard "Ugyldigt telefonnummer"; Klasse [] grupper () standard {}; Klasse [] nyttelast () standard {}; }

Med @ Constraint kommentar, vi definerede den klasse, der skal validere vores felt, besked() er den fejlmeddelelse, der vises i brugergrænsefladen, og den ekstra kode er den mest kedelpladekode, der overholder fjederstandarderne.

5. Oprettelse af en validator

Lad os nu oprette en validatorklasse, der håndhæver reglerne for vores validering:

offentlig klasse ContactNumberValidator implementerer ConstraintValidator {@Override public void initialize (ContactNumberConstraint contactNumber) {} @Override public boolean isValid (String contactField, ConstraintValidatorContext cxt) {return contactField! = null && contactField.matches ("[0-9] (contactField.length ()> 8) && (contactField.length () <14); }}

Valideringsklassen implementerer ConstraintValidator interface og skal implementere er gyldig metode; det er i denne metode, at vi definerede vores valideringsregler.

Naturligvis går vi med en simpel valideringsregel her for at vise, hvordan validatoren fungerer.

ConstraintValidator definerer logikken for at validere en given begrænsning for et givet objekt. Implementeringer skal overholde følgende begrænsning:

  • objektet skal løse til en ikke-parametreret type
  • generiske parametre for objektet skal være ubegrænsede jokertegnetyper

6. Anvendelse af valideringsanmærkninger

I vores tilfælde har vi oprettet en simpel klasse med et felt til at anvende valideringsreglerne. Her konfigurerer vi vores annoterede felt til validering:

@ContactNumberConstraint privat strengetelefon;

Vi definerede et strengfelt og kommenterede det med vores brugerdefinerede kommentar @ContactNumberConstraint. I vores controller oprettede vi vores kortlægninger og håndterede fejlen, hvis nogen:

@Controller public class ValidatedPhoneController {@GetMapping ("/ validatePhone") public String loadFormPage (Model m) {m.addAttribute ("validatedPhone", new ValidatedPhone ()); returner "phoneHome"; } @PostMapping ("/ addValidatePhone") public String submitForm (@Valid ValidatedPhone validatedPhone, BindingResult result, Model m) {if (result.hasErrors ()) {return "phoneHome"; } m.addAttribute ("besked", "Telefon gemt:" + validatedPhone.toString ()); returner "phoneHome"; }}

Vi definerede denne enkle controller, der har en enkelt JSP side, og brug sendForm metode til at håndhæve validering af vores telefonnummer.

7. Udsigten

Vores opfattelse er en grundlæggende JSP-side med en formular, der har et enkelt felt. Når brugeren sender formularen, bliver feltet valideret af vores brugerdefinerede validator og omdirigerer til den samme side med beskeden om vellykket eller mislykket validering:

 Telefon: 

8. Test

Lad os nu teste vores controller og kontrollere, om det giver os det passende svar og visning:

@Test offentligt ugyldigt givetPhonePageUri_whenMockMvc_thenReturnsPhonePage () {this.mockMvc. udfør (get ("/ validatePhone")). ogExpect (view (). navn ("phoneHome")); }

Lad os også teste, at vores felt er valideret, baseret på brugerinput:

@Test offentlig ugyldighed givenPhoneURIWithPostAndFormData_whenMockMVC_thenVerifyErrorResponse () {this.mockMvc.perform (MockMvcRequestBuilders.post ("/ addValidatePhone"). Accepter (MediaType.TEXT_HTML). "" ". andExpect (model (). attributeHasFieldErrorCode ("validatedPhone", "phone", "ContactNumberConstraint")). andExpect (view (). name ("phoneHome")). andExpect (status (). isOk ()). andDo (udskriv ()); }

I testen giver vi brugeren input af "123", og - som vi forventede - alt fungerer og vi ser fejlen på klientsiden.

9. Validering af brugerdefineret klasseniveau

En brugerdefineret valideringsnotering kan også defineres på klasseniveau for at validere mere end en attribut for klassen.

En almindelig brugssag for dette scenarie er at kontrollere, om to felter i en klasse har matchende værdier.

9.1. Oprettelse af kommentaren

Lad os tilføje en ny kommentar kaldet FieldsValueMatch der senere kan anvendes på en klasse. Annotationen har to parametre Mark og fieldMatch der repræsenterer navnene på de felter, der skal sammenlignes:

@Constraint (validatedBy = FieldsValueMatchValidator.class) @Target ({ElementType.TYPE}) @Retention (RetentionPolicy.RUNTIME) public @interface FieldsValueMatch {Strengmeddelelse () standard "Felterne stemmer ikke overens!"; Strengfelt (); String fieldMatch (); @Target ({ElementType.TYPE}) @Retention (RetentionPolicy.RUNTIME) @interface List {FieldsValueMatch [] value (); }}

Vi kan se, at vores brugerdefinerede kommentar også indeholder en Liste sub-interface til at definere flere FieldsValueMatch kommentarer til en klasse.

9.2. Oprettelse af validator

Dernæst skal vi tilføje FieldsValueMatchValidator klasse, der indeholder den faktiske valideringslogik:

offentlig klasse FieldsValueMatchValidator implementerer ConstraintValidator {privat strengfelt; private String fieldMatch; public void initialize (FieldsValueMatch constraintAnnotation) {this.field = constraintAnnotation.field (); this.fieldMatch = constraintAnnotation.fieldMatch (); } public boolean isValid (Object value, ConstraintValidatorContext context) {Object fieldValue = new BeanWrapperImpl (value) .getPropertyValue (field); Objekt fieldMatchValue = ny BeanWrapperImpl (værdi) .getPropertyValue (fieldMatch); hvis (fieldValue! = null) {return fieldValue.equals (fieldMatchValue); } andet {return fieldMatchValue == null; }}}

Det er gyldig() metode henter værdierne for de to felter og kontrollerer, om de er ens.

9.3. Anvendelse af kommentaren

Lad os oprette en NewUserForm modelklasse beregnet til data, der kræves til brugerregistrering, der har to e-mail og adgangskode attributter sammen med to Bekræft e-mail og Bekræft adgangskode attributter til at genindtaste de to værdier.

Da vi har to felter at kontrollere mod deres tilsvarende matchende felter, lad os tilføje to @FieldsValueMatch bemærkninger om Ny brugerform klasse, en til e-mail værdier og en til adgangskode værdier:

@ FieldsValueMatch.List ({@FieldsValueMatch (field = "password", fieldMatch = "verifyPassword", message = "Adgangskoder stemmer ikke overens!"), @FieldsValueMatch (field = "email", fieldMatch = "verificer e-mail", meddelelse = " E-mail-adresser stemmer ikke overens! ")}) Offentlig klasse NewUserForm {privat streng-e-mail; privat streng verificereE-mail; privat strengadgangskode; privat streng verificerePassword; // standard konstruktør, getters, setters}

Lad os oprette en controller med en for at validere modellen i Spring MVC /bruger POST-kortlægning, der modtager en Ny brugerform objekt kommenteret med @Gyldig og verificerer, om der er valideringsfejl:

@Controller public class NewUserController {@GetMapping ("/ user") public String loadFormPage (Model model) {model.addAttribute ("newUserForm", new NewUserForm ()); returner "userHome"; } @PostMapping ("/ bruger") offentlig String submitForm (@Valid NewUserForm newUserForm, BindingResult result, Model model) {if (result.hasErrors ()) {return "userHome"; } model.addAttribute ("besked", "Gyldig form"); returner "userHome"; }}

9.4. Test af kommentaren

Lad os skrive en for at bekræfte vores brugerdefinerede klassekommentarer JUnit test, der sender matchende information til /bruger slutpunkt, og verificerer derefter, at svaret ikke indeholder nogen fejl:

offentlig klasse ClassValidationMvcTest {private MockMvc mockMvc; @Før offentlig tomrumsopsætning () {this.mockMvc = MockMvcBuilders .standaloneSetup (ny NewUserController ()). Build (); } @Test offentlig ugyldighed givenMatchingEmailPassword_whenPostNewUserForm_thenOk () kaster undtagelse {this.mockMvc.perform (MockMvcRequestBuilders .post ("/ user") .accept (MediaType.TEXT_HTML). .Param ("e-mail", "[e-mail-beskyttet]".) ("verifyEmail", "[email protected]") .param ("password", "pass") .param ("verifyPassword", "pass")). andExpect (model (). errorCount (0)) .andExpect ( status (). isOk ()); }}

Lad os derefter tilføje en JUnit test, der sender ikke-matchende information til /bruger slutpunkt og hævde, at resultatet vil indeholde to fejl:

@Test offentlig ugyldighed givenNotMatchingEmailPassword_whenPostNewUserForm_thenOk () kaster undtagelse {this.mockMvc.perform (MockMvcRequestBuilders .post ("/ user") .accept (MediaType.TEXT_HTML) .param ("e-mail", "[e-mail-beskyttet]".) verifyEmail "," [email protected] ") .param (" password "," pass ") .param (" verifyPassword "," passsss ")). andExpect (model (). errorCount (2)). andExpect (status ( ) .isOk ()); }

10. Resume

I denne hurtige artikel har vi vist, hvordan man opretter brugerdefinerede validatorer til at verificere et felt eller en klasse og koble dem til Spring MVC.

Som altid kan du finde koden fra artiklen på Github.