验证

Spring MVC 为 @RequestMapping 方法提供了内置的验证支持,包括Java Bean Validation。验证可以在两个级别中的一个应用:

  1. @ModelAttribute@RequestBody@RequestPart 参数解析器会单独验证方法参数,前提是方法参数带有 Jakarta 的 @Valid 或 Spring 的 @Validated 注解,并且后面没有紧跟着 ErrorsBindingResult 参数,并且不需要方法验证(接下来讨论)。在这种情况下抛出的异常是 MethodArgumentNotValidException

  2. @Constraint 注解(例如 @Min@NotBlank 等)直接声明在方法参数上,或者方法上(用于返回值),则必须应用方法验证。这会取代方法参数级别的验证,因为方法验证涵盖了方法参数约束和通过 @Valid 实现的嵌套约束。在这种情况下抛出的异常是 HandlerMethodValidationException

应用程序必须处理 MethodArgumentNotValidExceptionHandlerMethodValidationException,因为根据控制器方法签名,两者都可能被抛出。然而,这两种异常的设计非常相似,可以使用几乎相同的代码来处理。主要区别在于前者是针对单个对象,而后者是针对一组方法参数。

@Valid 不是一个约束注解,而是用于对象内的嵌套约束。因此,@Valid 本身不会导致方法验证。另一方面,@NotNull 是一个约束,将其添加到 @Valid 参数会导致方法验证。对于空性(nullability)而言,您也可以使用 @RequestBody@ModelAttributerequired 标志。

方法验证可以与 ErrorsBindingResult 方法参数结合使用。然而,只有当所有验证错误都发生在紧随其后带有 Errors 参数的方法参数上时,控制器方法才会被调用。如果其他任何方法参数上存在验证错误,则会抛出 HandlerMethodValidationException

您可以通过WebMvc 配置全局配置 Validator,或者通过 @Controller@ControllerAdvice 中的 @InitBinder 方法进行局部配置。您也可以使用多个验证器。

如果一个控制器有类级别的 @Validated 注解,那么方法验证会通过 AOP 代理应用。为了利用 Spring Framework 6.1 中添加的 Spring MVC 内置方法验证支持,您需要从控制器中移除类级别的 @Validated 注解。

错误响应一节提供了关于如何处理 MethodArgumentNotValidExceptionHandlerMethodValidationException 的更多细节,以及如何通过 MessageSource 和区域/语言特定的资源包来自定义它们的呈现方式。

为了进一步自定义处理方法验证错误,您可以扩展 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) {
		// ...
	}
})