使用SpringMVC时配合hibernate-validate进行参数的合法性校验【常规性校验】,能节省一定的代码量.
使用步骤
1.搭建Web工程并引入hibernate-validate依赖
Maven依赖传递,自动依赖validation-api、jboss-logging、classmate
2.使用校验注解标注在属性上(dto)
*每个注解都有message属性,该属性用于填写校验失败时的异常描述信息,当校验失败时可以获取对应的message属性值.
3.控制层中使用dto接收参数并使用@Validated/@Valid注解开启对参数的校验
支持分组校验,声明在入参上.
不支持分组校验,声明在入参上.
*在dto后面要紧跟BindingResult对象,该对象用于获取当校验失败时的异常信息.
演示:
结果:
*校验的顺序是随机的,因此程序不能依赖校验的顺序去做相关的逻辑处理.
4.分组校验
每个校验注解都有group属性用于指定校验所属的组,其值是Class数组,在Controller中使用@Validated注解开启对参数的校验时当指定要进行校验的组,那么只有组相同的属性才会被进行校验(默认全匹配).
一般定义标识接口作为组资源
使用校验注解标注在属性上并进行分组
只有组相同的属性才会被进行校验(默认全匹配),
演示:
Spring MVC - @Valid on list of beans in REST service
Where the MyBean class has bean validation annotations.
The validations don't seem to take place in this case, although it works well for other controllers.
解决办法:
(1)Wrap your list inside a Java Bean //把这个List放到一个Java Bean中
(2)Call the validator manually in your bulk create method myEntityValidator. validate(targetObject, errors). //显式使用spring的Validator解决
问题原因【在Spring Validation中List不是一个Java Bean】:
As you might have guessed this cannot be achieved using Spring Validation.
Spring Validation implements Bean Validation(JSR 303/349) as opposed to Object validation.
Unfortunately a collection is not a Java Bean.
https://stackoverflow.com/questions/34011892/spring-validation-for-requestbody-parameters-bound-to-collections-in-controller/36790509#answer-36790509
来看看Java Bean的定义:
https://docs.oracle.com/javaee/5/tutorial/doc/bnair.html
解决方案1:Wrap your list inside a Java Bean
(1)具体代码实现1【问题:会影响input的json数据结构】
https://stackoverflow.com/questions/17207766/spring-mvc-valid-on-list-of-beans-in-rest-service把上面的改成这样:
and we also need:
(2)具体代码实现【不改变input代码现实,但会有可读性、可维护性的难度】
继承java.util.ArrayList或实现java.util.List,实现java.util.List会有好多接口需要实现,理解和操作难度会更高
https://stackoverflow.com/questions/49876901/how-to-validate-a-collection-in-spring-mvc-post-webservice?noredirect=1&lq=1实现java.util.List接口的示例:
https://stackoverflow.com/questions/28150405/validation-of-a-list-of-objects-in-spring?noredirect=1&lq=1
解决方案2:
Call the validator manually in your bulk create method myEntityValidator. validate(targetObject, errors). //显式使用spring的Validator解决
具体代码实现(1):
The solution is to create a custom Validator for Collection and a @ControllerAdvice that registers that Validator in the WebDataBinders.
Validator:
在@ControllerAdvice中注册上面为java.util.Collection定制的CollectionValidator
另一个写法,待测试:
Try direct validation. Something like this:
https://stackoverflow.com/questions/17207766/spring-mvc-valid-on-list-of-beans-in-rest-service
直接使用Java BeanValidation中示例代码:
JavaBean Validation - Object Association validation with @Valid
According to the Bean Validation specification, the @Valid annotation on a given object reference is used to allow cascading Validation. The associated object can itself contain cascaded references, hence it is a recursive process. This feature is also referred as 'object graph validation'.
Example
In this example, we are purposely supplying invalid values during object creation to see @Valid annotation in action.
Output:
2 violations:
driver.dateOfBirth must be in the past
driver.height must be greater than or equal to 100
https://www.logicbig.com/tutorials/java-ee-tutorial/bean-validation/cascaded-validation.html
JavaBean Validation - Collection Validation
Just like object references can be validated recursively by using @Valid (as we saw in the last example), the elements of Java Collections, arrays and Iterable can also be validated by using @Valid annotation.
Output:
As seen, the List elements (employees) were validated as expected.
https://www.logicbig.com/tutorials/java-ee-tutorial/bean-validation/collection-validation.html
Let's remove @Valid annotation in above example:
Output:
https://www.logicbig.com/tutorials/java-ee-tutorial/bean-validation/collection-validation.html
@Valid
is a JSR-303 annotation and JSR-303 applies to validation on JavaBeans. A java.util.List
is not a JavaBean (according to the official description of a JavaBean), hence it cannot be validated directly using a JSR-303 compliant validator. This is supported by two observations.
Section 3.1.3 of the JSR-303 Specification says that:
In addition to supporting instance validation, validation of graphs of object is also supported. The result of a graph validation is returned as a unified set of constraint violations. Consider the situation where bean X contains a field of type Y. By annotating field Y with the @Valid annotation, the Validator will validate Y (and its properties) when X is validated. The exact type Z of the value contained in the field declared of type Y (subclass, implementation) is determined at runtime. The constraint definitions of Z are used. This ensures proper polymorphic behavior for associations marked @Valid.
Collection-valued, array-valued and generally Iterable fields and properties may also be decorated with the @Valid annotation. This causes the contents of the iterator to be validated. Any object implementing java.lang.Iterable is supported.
I have marked the important pieces of information in bold. This section implies that in order for a collection type to be validated, it must be encapsulated inside a bean (implied by Consider the situation where bean X contains a field of type Y
); and further that collections cannot be validated directly (implied by Collection-valued, array-valued and generally Iterable fields and properties may also be decorated
, with emphasis on fields and properties).
Actual JSR-303 implementations
I have a sample application that tests collection validation with both Hibernate Validator and Apache Beans Validator. If you run tests on this sample as mvn clean test -Phibernate
(with Hibernate Validator) and mvn clean test -Papache
(for Beans Validator), both refuse to validate collections directly, which seems to be in line with the specification. Since Hibernate Validator is the reference implementation for JSR-303, this sample is further proof that collections need to be encapsulated in a bean in order to be validated.
https://stackoverflow.com/questions/17207766/spring-mvc-valid-on-list-of-beans-in-rest-service
3. Validation, Data Binding, and Type Conversion
There are pros and cons for considering validation as business logic, and Spring offers a design for validation (and data binding) that does not exclude either one of them. Specifically, validation should not be tied to the web tier and should be easy to localize, and it should be possible to plug in any available validator. Considering these concerns, Spring has come up with a Validator
interface that is both basic and eminently usable in every layer of an application.
Data binding is useful for letting user input be dynamically bound to the domain model of an application (or whatever objects you use to process user input). Spring provides the aptly named DataBinder
to do exactly that. The Validator
and theDataBinder
make up the validation
package, which is primarily used in but not limited to the MVC framework.
The BeanWrapper
is a fundamental concept in the Spring Framework and is used in a lot of places. However, you probably do not need to use the BeanWrapper
directly. Because this is reference documentation, however, we felt that some explanation might be in order. We explain the BeanWrapper
in this chapter, since, if you are going to use it at all, you are most likely do so when trying to bind data to objects.
Spring’s DataBinder
and the lower-level BeanWrapper
both use PropertyEditorSupport
implementations to parse and format property values. The PropertyEditor
and PropertyEditorSupport
interfaces are part of the JavaBeans specification and are also explained in this chapter. Spring 3 introduced a core.convert
package that provides a general type conversion facility, as well as a higher-level “format” package for formatting UI field values. You can use these packages as simpler alternatives to PropertyEditorSupport
implementations. They are also discussed in this chapter.
JSR-303/JSR-349 Bean Validation
As of version 4.0, Spring Frameworksupports Bean Validation 1.0 (JSR-303) and Bean Validation 1.1 (JSR-349) for setup support and adapting them to Spring’s Validator
interface.
An application can choose to enable Bean Validation once globally, as described in Spring Validation, and use it exclusively for all validation needs.
An application can also register additional Spring Validator
instances for each DataBinder
instance, as described in Configuring a DataBinder. This may be useful for plugging in validation logic without the use of annotations.
3.1. Validation by Using Spring’s Validator Interface
Spring features a Validator
interface that you can use to validate objects. The Validator
interface works by using an Errors
object so that, while validating, validators can report validation failures to the Errors
object.
Consider the following example of a small data object:
The next example provides validation behavior for the Person
class by implementing the following two methods of the org.springframework.validation.Validator
interface:
-
supports(Class)
: Can this Validator
validate instances of the supplied Class
? -
validate(Object, org.springframework.validation.Errors)
: Validates the given object and, in case of validation errors, registers those with the given Errors
object.
Implementing a Validator
is fairly straightforward, especially when you know of the ValidationUtils
helper class that the Spring Framework also provides. The following example implements Validator
for Person
instances:
The static
rejectIfEmpty(..)
method on the ValidationUtils
class is used to reject the name
property if it is null
or the empty string. Have a look at the ValidationUtils javadoc to see what functionality it provides besides the example shown previously.
While it is certainly possible to implement a single Validator
class to validate each of the nested objects in a rich object, it may be better to encapsulate the validation logic for each nested class of object in its own Validator
implementation. A simple example of a “rich” object would be a Customer
that is composed of two String
properties (a first and a second name) and a complex Address
object. Address
objects may be used independently of Customer
objects, so a distinct AddressValidator
has been implemented. If you want your CustomerValidator
to reuse the logic contained within the AddressValidator
class without resorting to copy-and-paste, you can dependency-inject or instantiate an AddressValidator
within your CustomerValidator
, as the following example shows:
Validation errors are reported to the Errors
object passed to the validator. In the case of Spring Web MVC, you can use the <spring:bind/>
tag to inspect the error messages, but you can also inspect the Errors
object yourself. More information about the methods it offers can be found in the javadoc.
3.2. Resolving Codes to Error Messages
We covered databinding and validation. This section covers outputting messages that correspond to validation errors. In the example shown in the preceding section, we rejected the name
and age
fields. If we want to output the error messages by using a MessageSource
, we can do so using the error code we provide when rejecting the field ('name' and 'age' in this case). When you call (either directly, or indirectly, by using, for example, the ValidationUtils
class) rejectValue
or one of the other reject
methods from the Errors
interface, the underlying implementation not only registers the code you passed in but also registers a number of additional error codes. The MessageCodesResolver
determines which error codes the Errors
interface registers. By default, the DefaultMessageCodesResolver
is used, which (for example) not only registers a message with the code you gave but also registers messages that include the field name you passed to the reject method. So, if you reject a field by usingrejectValue("age", "too.darn.old")
, apart from the too.darn.old
code, Spring also registers too.darn.old.age
and too.darn.old.age.int
(the first includes the field name and the second includes the type of the field). This is done as a convenience to aid developers when targeting error messages.
More information on the MessageCodesResolver
and the default strategy can be found in the javadoc of MessageCodesResolverand DefaultMessageCodesResolver, respectively.
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation