L'annotation JSR-303 @Valid ne fonctionne pas pour la liste des objets enfants

95

Mes principales classes sont

public class UserAddressesForm {

    @NotEmpty
    private String firstName;

    @NotEmpty
    private String lastName;

    private List<AddressForm> addresses;

...
setters and getters 

public class AddressForm {

    @NotEmpty
    private String customName;
    @NotEmpty
    private String city;
    @NotEmpty
    private String streetAn;
    @NotEmpty
    private String streetHn;
    @NotEmpty
    private String addressCountry;
    @NotEmpty
    private String postCode;
...
setters and getters

Un contrôleur

@RequestMapping(value = "/up", method = RequestMethod.POST)
    public String completeForm(@Valid @ModelAttribute("userAddressesForm") UserAddressesForm userAddressesForm,  
            BindingResult result, HttpServletRequest req)
...

Une page JSP

<form:form commandName="userAddressesForm" action="registered">
    <table>

        <tr>
            <td class="formLabels"><form:label path="firstName">
                <spring:message code="label.name" />
            </form:label></td>
            <td><form:input path="firstName" /></td>
            <td><form:errors path="firstName" cssClass="error" /></td>
        </tr>
        <tr>
            <td class="formLabels"><form:label path="lastName">
                <spring:message code="label.surname" />
            </form:label></td>
            <td><form:input path="lastName" /></td>
            <td><form:errors path="lastName" cssClass="error" /></td>
        </tr>
    </table>

    <c:forEach items="${userAddressesForm.addresses}" varStatus="gridRow">  
        <div id="main_address" class="address_data_form">
            <fieldset>
                <legend><spring:message code="label.stepThreeMainAddressInfo" /></legend>
                <a href="#" class="deleteItem"></a>
                <table>
                    <tr>            
                        <td class="formLabels">
                            <spring:message code="label.address.custom.name" />
                        </td>
                        <td>
                            <spring:bind path="addresses[${gridRow.index}].customName">
                                <input type="input" name="<c:out value="${status.expression}"/>"
                                    id="<c:out value="${status.expression}"/>"
                                    value="<c:out value="${status.value}"/>" />
                                    <form:errors path="${status.expression}"/>
                            </spring:bind>
                        </td>   
                    </tr>               
                    <tr>            
                        <td class="formLabels">
                            <spring:message code="label.streetAnStreetHn" />
                        </td>
                        <td>
                            <spring:bind path="addresses[${gridRow.index}].streetAn">
                                <input type="input" name="<c:out value="${status.expression}"/>"
                                    id="<c:out value="${status.expression}"/>"
                                    value="<c:out value="${status.value}"/>" />
                            </spring:bind>
                            <spring:bind path="addresses[${gridRow.index}].streetHn">
                            <input type="input" name="<c:out value="${status.expression}"/>"
                                id="<c:out value="${status.expression}"/>"
                                value="<c:out value="${status.value}"/>" >
                            <form:errors path="addresses[${gridRow.index}].streetHn"/>
                            </spring:bind>

                        </td>
                    </tr>
                    <tr>                        
                        <td class="formLabels">
                            <spring:message code="label.postCode" />
                        </td>
                        <td>
                            <spring:bind path="addresses[${gridRow.index}].postCode">
                                <input type="input" name="<c:out value="${status.expression}"/>"
                                    id="<c:out value="${status.expression}"/>"
                                    value="<c:out value="${status.value}"/>" />
                            </spring:bind>
                        </td>                   
                    </tr>
                    <tr>                
                        <td class="formLabels">
                            <spring:message code="label.city" />
                        </td>
                        <td>
                            <spring:bind path="addresses[${gridRow.index}].city">
                                <input type="input" name="<c:out value="${status.expression}"/>"
                                    id="<c:out value="${status.expression}"/>"
                                    value="<c:out value="${status.value}"/>" />
                                <form:errors path="addresses[${gridRow.index}].city" cssClass="error" />
                            </spring:bind>
                        </td>
                    </tr>       
                </table>    
            </fieldset>
        </div>
    </c:forEach>

Pourquoi les champs d'objet ne sont pas validés AddressForm?

Veuillez aider.

user6778654
la source

Réponses:

165

Vous devez décorer le addressesmembre UserAddressesFormavec une @Validannotation. Voir les sections 3.1.3 et 3.5.1 de JSR 303: Bean Validation . Comme je l'ai expliqué dans ma réponse à la question Existe-t-il un moyen standard d'activer la validation de bean JSR 303 à l'aide de la méthode annotée , c'est l'utilisation réelle de@Valid annotation selon JSR 303.

Edit Exemple de code: Hibernate Validator - Object Graph . (La liste des passagers en voiture)

Modifier à partir d' Hibernate Validator 6 Document de référence:

Dans les versions antérieures à 6, Hibernate Validator prenait en charge la validation en cascade pour un sous-ensemble d'éléments de conteneur et il était implémenté au niveau du conteneur (par exemple, vous utiliseriez @Valid private List<Person>pour activer la validation en cascade pour Person).

Ceci est toujours pris en charge mais n'est pas recommandé. Veuillez @Validplutôt utiliser des annotations au niveau des éléments de conteneur car elles sont plus expressives.

Exemple:

public class Car {

        private List<@NotNull @Valid Person> passengers = new ArrayList<Person>();

        private Map<@Valid Part, List<@Valid Manufacturer>> partManufacturers = new HashMap<>();

       //...
   }

Découvrez également les nouveautés de Bean Validation 2.0 / Jakarta Bean Validation .

Ritesh
la source
Et le groupement? Je souhaite que l'adresse ne soit validée que lorsque le groupe A est validé. @Validignore le groupe.
Akshay
1
L' @Validannotation @Akshay fonctionne au niveau inférieur. Je pense que @GroupSequence pourrait être utilisé pour spécifier l'ordre de validation.
Ritesh
Ma question détaillée stackoverflow.com/q/41457404/1534925 . Ne pensez pas que la séquence fonctionnerait dans mon cas.
Akshay
27

En ajoutant à la réponse @Ritesh, la @Validcontrainte demandera au validateur de bean d'explorer le type de sa propriété appliquée et de valider toutes les contraintes qui s'y trouvent. Répondez avec du code à votre question, le validateur, en voyant une @Validcontrainte sur la addressespropriété, explorera la AddressFormclasse et validera toutes les JSR 303contraintes trouvées à l'intérieur, comme suit:

public class UserAddressesForm {

    @NotEmpty
    private String firstName;

    @NotEmpty
    private String lastName;

    @Valid
    private List<AddressForm> addresses;

...
setters and getters 

public class AddressForm {

    @NotEmpty
    private String customName;
    @NotEmpty
    private String city;
    @NotEmpty
    private String streetAn;
    @NotEmpty
    private String streetHn;
    @NotEmpty
    private String addressCountry;
    @NotEmpty
    private String postCode;
...
setters and getters
Arpit Aggarwal
la source
1
Et le groupement? Je souhaite que l'adresse ne soit validée que lorsque le groupe A est validé. @Valid ignore le groupe
Akshay
Je n'ai même pas lu votre réponse, mais simplement en regardant setters and gettersrésolu mon problème.
Shubham Khandare
8

Dans la classe UserAddressesForm, ajoutez les lignes suivantes

@Valid
private List<AddressForm> addresses;
Banquier Tapan
la source
4

Solution de travail.

public class UserAddressesForm {

    @NotEmpty(message="firstName is required")
    private String firstName;

    @NotEmpty(message="lastNameis required")
    private String lastName;

    @NotNull(message="addresses attributes are required")
    @Valid
    private List<AddressForm> addresses;

...
setters and getters 

public class AddressForm {

    @NotEmpty(message="customNameis required")
    private String customName;
    @NotEmpty
    private String city;
    @NotEmpty
    private String streetAn;
    @NotEmpty
    private String streetHn;
    @NotEmpty
    private String addressCountry;
    @NotEmpty
    private String postCode;
...
setters and getters
Waqas Ahmed
la source