验证
Spring MVC为@RequestMapping方法内置了验证功能,包括Java Bean Validation。验证可以在两个层次之一进行:
-
Java Bean Validation可以单独应用于被
@jakarta.validation.Valid或Spring的@Validated注解标注的[@ModelAttribute(ann-methods/modelattrib-method-args.md), [@RequestBody(ann-methods/requestbody.md), 和 [@RequestPart(ann-methods/multipart-forms.md))方法参数,前提是这些参数是命令对象(command object),而不是Map或Collection这样的容器;同时,方法签名中不能紧跟着这些参数有Errors或BindingResult;此外,这些参数也不需要其他形式的参数验证(详见下文)。当单独验证方法参数时,会抛出MethodArgumentNotValidException`异常。 -
当
@Min、@Blank等@Constraint注解直接标注在方法参数上,或者标注在方法的返回值上时,Java Bean Validation也会被应用到该方法上。在这种情况下,该方法参数的验证将优先于单独对参数进行的任何其他验证,因为方法验证通过@Valid同时覆盖了方法参数自身的约束以及嵌套的约束。当验证应用于方法时,会抛出HandlerMethodValidationException异常。
应用程序应该同时处理MethodArgumentNotValidException和HandlerMethodValidationException,因为根据控制器方法的签名,这两种异常中的任何一种都可能被抛出。然而,这两种异常的设计非常相似,可以用几乎相同的代码来处理。主要的区别在于,前者是针对单个对象的,而后者则是针对方法参数列表的。
@Valid 并不是一种约束注解,而是用于对象内部的嵌套约束。因此,单独使用 @Valid 并不会引发方法验证。而 @NotNull 则是一种约束注解,将其添加到带有 @Valid 的参数上会触发方法验证。对于可空性(nullability)的具体处理,你也可以使用 @RequestBody 或 @ModelAttribute 中的 required 标志。
方法验证可以与Errors或BindingResult方法参数结合使用。但是,只有当所有验证错误都出现在方法参数上,并且紧随其后有Errors时,控制器方法才会被调用。如果任何其他方法参数存在验证错误,则会抛出HandlerMethodValidationException异常。
你可以通过WebMvc配置全局配置一个Validator,或者通过在@Controller或@ControllerAdvice中的@InitBinder方法来局部配置。你也可以使用多个验证器。
如果一个控制器有类级别的@Validated注解,那么方法验证将通过AOP代理被应用。为了利用Spring Framework 6.1中添加的Spring MVC内置方法验证支持,你需要从控制器中移除类级别的@Validated注解。
错误响应部分提供了关于如何处理MethodArgumentNotValidException和HandlerMethodValidationException的更多细节,同时也介绍了如何通过MessageSource以及特定于区域设置(locale)和语言的资源包来自定义这些异常的显示方式。
对于方法验证错误的进一步自定义处理,您可以扩展ResponseEntityExceptionHandler,或者在控制器中或@ControllerAdvice中使用@ExceptionHandler方法,直接处理HandlerMethodValidationException异常。该异常包含一个ParameterValidationResult列表,这些结果按方法参数对验证错误进行分组。您可以遍历这些结果,或者为控制器方法参数类型提供相应的回调方法(即访问器)。
- Java
- Kotlin
HandlerMethodValidationException ex = ... ;
ex.visitResults(new HandlerMethodValidationException.Visitor() {
@Override
public void requestHeader(RequestHeader requestHeader, ParameterValidationResult result) {
// ...
}
@Override
public void requestParam(@Nullable RequestParam requestParam, ParameterValidationResult result) {
// ...
}
@Override
public void modelAttribute(@Nullable ModelAttribute modelAttribute, ParameterErrors errors) {
// ...
@Override
public void other(ParameterValidationResult result) {
// ...
}
});
// HandlerMethodValidationException
val ex
ex.visitResults(object : HandlerMethodValidationException.Visitor {
override fun requestHeader(requestHeader: RequestHeader, result: ParameterValidationResult) {
// ...
}
override fun requestParam(requestParam: RequestParam?, result: ParameterValidationResult) {
// ...
}
override fun modelAttribute(modelAttribute: ModelAttribute?, errors: ParameterErrors) {
// ...
}
// ...
override fun other(result: ParameterValidationResult) {
// ...
}
})