Co oznacza adnotacja @Valid wiosną?

88

W poniższym przykładzie ScriptFileparametr jest oznaczony @Validadnotacją.

Co robi @Validadnotacja?

@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();        
}    
Gary Ford
źródło

Odpowiedzi:

63

To jest do celów walidacji.

Walidacja Często sprawdza się poprawność modelu po powiązaniu z nim danych wejściowych użytkownika. Spring 3 zapewnia obsługę deklaratywnej walidacji z JSR-303. Ta obsługa jest włączana automatycznie, jeśli dostawca JSR-303, taki jak Hibernate Validator, jest obecny w ścieżce klas. Po włączeniu można wyzwolić walidację, po prostu dodając adnotację do parametru metody kontrolera z adnotacją @Valid: Po powiązaniu przychodzących parametrów POST, zostanie sprawdzona poprawność AppointmentForm; w tym przypadku, aby sprawdzić, czy wartość pola daty nie jest zerowa i występuje w przyszłości.


Poszukaj więcej informacji:
http://blog.springsource.com/2009/11/17/spring-3-type-conversion-and-validation/

mhshams
źródło
37

Dodając powyższe odpowiedzi, spójrz na poniższe. AppointmentForm„s datekolumna jest opatrzone kilka adnotacji. Mając @Validadnotację, która wyzwala walidację na AppointmentForm(w tym przypadku @NotNulli @Future). Te adnotacje mogą pochodzić od różnych dostawców JSR-303 (np. 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;
    }
Charith De Silva
źródło
1
Mam podobny kod. I usunął @Validna ApplicationFormparametr, ale nadal validations padły na date(ustawionej jako null) pola. Proszę wytłumacz.
lupchiazoem
18

@Validsamo w sobie nie ma nic wspólnego ze Springiem. Jest to część specyfikacji Bean Validation (jest ich kilka, najnowsza to JSR 380 na drugą połowę 2017 roku), ale @Validjest bardzo stara i wywodzi się w całości z JSR 303.

Jak wszyscy wiemy, Spring jest bardzo dobry w zapewnianiu integracji ze wszystkimi różnymi JSR i ogólnie bibliotekami java (pomyśl o JPA, JTA, buforowaniu itp.) I oczywiście ci faceci zadbali również o walidację. Jednym z kluczowych składników, który to ułatwia, jest MethodValidationPostProcessor .

Próba odpowiedzi na Twoje pytanie - @Validjest bardzo przydatna w przypadku tzw. Kaskadowania walidacji, gdy chcesz zweryfikować złożony wykres, a nie tylko elementy obiektu najwyższego poziomu. Za każdym razem, gdy chcesz wejść głębiej, musisz użyć @Valid. Tak nakazuje JSR. Spring dostosuje się do tego z pewnymi drobnymi odchyleniami (np. Próbowałem @Validatedzamiast @ValidRestController wstawić metodę i walidację, ale to samo nie będzie dotyczyło zwykłych "usługowych" fasoli).

yuranos
źródło
Ale co to potwierdza?
nephewtom
Proszę rozwinąć, @nephewtom. Co chcesz wyjaśnić?
yuranos
Czytałem JSR 303: Bean Validation, beanvalidation.org/1.0/spec, ale nadal nie wiem, na jakiej walidacji zostanie przeprowadzona ScriptFile scriptFile.
nephewtom
Wewnątrz pliku ScriptFile prawdopodobnie znajduje się kilka pól, a te pola również mają adnotacje. W tym miejscu rozpoczyna się walidacja. Na podstawie pierwotnego pytania nie jest jasne, co dokładnie znajduje się w klasie ScriptFile.
yuranos
Ok dzięki. Czy mógłbyś podać przykład tego, co mogłoby zweryfikować, gdyby jego pola były typu String, Integer i Bean?
nephewtom
17

IIRC @Valid nie jest adnotacją Spring, ale adnotacją JSR-303 (która jest standardem Bean Validation). Po prostu sprawdza, czy dane wysyłane do metody są prawidłowe, czy nie (zweryfikuje plik scriptFile za Ciebie).

Mateusz Dymczyk
źródło
2
Co to znaczy „zweryfikuje plik scriptFile”? Sprawdź, czy nie jest null, ma jakąś składnię lub jakąś zawartość? Innymi słowy, co potwierdza i jak? Czy użytkownik powinien coś wdrożyć? Gdzie mogę uzyskać informacje na ten temat? Dziękuję Ci!
nephewtom
Odpowiedz na pytanie @ nephewtom. Odpowiedź nie jest wystarczająca, aby potwierdzić brakujący główny punkt, czyli „przeciwko czemu”?
monami
2
public String create(@Valid @NotNull ScriptFile scriptFile, BindingResult result, ModelMap modelMap) {    
    if (scriptFile == null) throw new IllegalArgumentException("A scriptFile is required");        

Myślę, że ta @NotNulladnotacja jest ważna, więc jeśli warunek nie jest potrzebny.

mzlobecki
źródło
2

Myślę, że wiem, dokąd zmierza twoje pytanie. A ponieważ to pytanie pojawia się w głównych wynikach wyszukiwania Google, mogę udzielić prostej odpowiedzi na temat tego, co robi adnotacja @Valid.

Przedstawię 3 scenariusze dotyczące korzystania z @Valid

Model:

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>
...

Kontroler dla scenariusza 1:

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

W tym scenariuszu po przesłaniu formularza z pustym polem lastName zostanie wyświetlona strona błędu, ponieważ stosujesz reguły walidacji, ale w ogóle jej nie obsługujesz.

Przykład błędu: strona wyjątków

Kontroler dla scenariusza 2:

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

W tym scenariuszu przekazujesz wszystkie wyniki z tego walidacji do bindingResult, więc od Ciebie zależy, co zrobić z wynikami walidacji tego formularza.

Kontroler dla scenariusza 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
}

W tym scenariuszu nadal nie obsługujesz błędów, jak w pierwszym scenariuszu, ale przekazujesz to do innej metody, która zajmie się wyjątkiem, który wyzwala @Valid podczas przetwarzania modelu formularza. Sprawdź to, zobacz, co zrobić z mapowaniem i tak dalej.

Podsumowując : @Valid samodzielnie, nie robiąc nic więcej, co wyzwala walidację pól z adnotacjami JSR 303 ( @NotNull, @Email, @Size, itp. ), Nadal musisz określić strategię tego, co robić z wynikami tej walidacji.

Mam nadzieję, że udało mi się wyjaśnić coś dla ludzi, którzy mogą się z tym potknąć.

Alan Balderas Sánchez
źródło
1

Wystarczy dodać do powyższej odpowiedzi, w aplikacji internetowej @validjest stosowana, gdy fasola być weryfikowane są również opatrzone walidacja adnotacje np @NotNull, @Email(hibernacji adnotacja), więc gdy przy pobieraniu wejście od użytkownika wartości mogą być zatwierdzone i wiążący wynik będzie miał walidacji wyniki. bindingResult.hasErrors()powie, jeśli jakakolwiek weryfikacja się nie powiodła.

puneet goyal
źródło
1

Innym przydatnym aspektem @Valid, o którym nie wspomniano powyżej, jest to, że (np. Użycie Postmana do testowania punktu końcowego) @Valid sformatuje wynik nieprawidłowego wywołania REST do sformatowanego JSON zamiast bloba ledwo czytelnego tekstu. Jest to bardzo przydatne, jeśli tworzysz komercyjny interfejs API dla swoich użytkowników.

Sean Gildea
źródło
1

Chciałem dodać więcej szczegółów na temat tego, jak @Validdziała, zwłaszcza wiosną.

Wszystko, co chciałbyś wiedzieć o walidacji na wiosnę, jest jasno i szczegółowo wyjaśnione w https://reflectoring.io/bean-validation-with-spring-boot/ , ale skopiuję odpowiedź, jak @Validdziała, w przypadku linku idzie w dół.

@ValidAdnotacji mogą być dodawane do zmiennych w spoczynku metody kontrolera, aby je zatwierdzić. Istnieją 3 typy zmiennych, które można zweryfikować:

  • treść wniosku,
  • zmienne w ścieżce (np. id in / foos / {id}) oraz
  • parametry zapytania.

Więc teraz… w jaki sposób wiosna "sprawdza się"? Możesz zdefiniować ograniczenia dla pól klasy, dodając do nich adnotacje. Następnie przekazujesz obiekt tej klasy do walidatora, który sprawdza, czy ograniczenia są spełnione.

Na przykład załóżmy, że mam taką metodę kontrolera:

@RestController
class ValidateRequestBodyController {

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

}

Jest to więc żądanie POST, które przyjmuje treść odpowiedzi i mapujemy tę treść odpowiedzi na klasę Input.

Oto klasa 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;
  
  // ...
}

Adnotacja @Valid poinformuje spring, aby przeszła i zweryfikowała dane przekazane do kontrolera, sprawdzając, czy liczba całkowita numberBetweenOneAndTenmieści się w zakresie od 1 do 10 włącznie z powodu tych adnotacji min i max. Sprawdza również, czy przekazany adres IP pasuje do wyrażenia regularnego w adnotacji.

uwaga boczna: wyrażenie regularne nie jest doskonałe… możesz podać 3-cyfrowe liczby większe niż 255 i nadal pasowałoby do wyrażenia regularnego.


Oto przykład sprawdzania poprawności zmiennej zapytania i zmiennej ścieżki:

@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");
  }
}

W tym przypadku, ponieważ zmienna zapytania i zmienna ścieżki są po prostu liczbami całkowitymi, a nie tylko złożonymi klasami, umieszczamy adnotację ograniczenia @Min(5)bezpośrednio na parametrze zamiast używać @Valid.

Jakiś facet
źródło