@ Valid 注释不验证子对象列表

主要模型类别如下:

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
}

我的一个控制器中的一个端点:

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


// logic here


}

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

为什么 @Valid不验证 UserAddressesForm类中存在的 List<AddressForm> addresses

68606 次浏览

You need to decorate addresses member of UserAddressesForm with @Valid annotation. See section 3.1.3 and 3.5.1 of JSR 303: Bean Validation. As I explained in my answer to the question Is there a standard way to enable JSR 303 Bean Validation using annotated method, this is the real use of @Valid annotation as per JSR 303.

Edit Example code: Hibernate Validator- Object Graph. (The list of passengers in Car)

Edit From Hibernate Validator 6 Reference doc:

In versions prior to 6, Hibernate Validator supported cascaded validation for a subset of container elements and it was implemented at the container level (e.g. you would use @Valid private List<Person> to enable cascaded validation for Person).

This is still supported but is not recommended. Please use container element level @Valid annotations instead as it is more expressive.

Example:

public class Car {


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


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


//...
}

Also see what's new in Bean Validation 2.0/Jakarta Bean Validation.

Adding to @Ritesh answer, @Valid constraint will instruct the Bean Validator to delve to the type of its applied property and validate all constraints found there. Answer with code to your question, the validator, when seeing a @Valid constraint on addresses property, will explore the AddressForm class and validate all JSR 303 constraints found inside, as follows:

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

In the class UserAddressesForm add the following lines

@Valid
private List<AddressForm> addresses;

Working solution.

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

I implemented all the suggestions and annotated child field in the parent class with with @Valid but still the child entities were not getting validated.

Luckily it is resolved now and the issue was because of the dependency. As a solution I used below dependency

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

instead of

<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>

For me, it was @NotNull, in the child, and not a @NotEmpty annotation. I had to put a @NotNull before the @Valid annontation to make it work.

like this:

private List<@NotNull @Valid YourObject> yourObjects;