Add method validation to Spring MVC

See gh-29825
This commit is contained in:
Rossen Stoyanchev
2023-06-08 15:39:22 +01:00
committed by rstoyanchev
parent cb04c3b335
commit bd054a4918
15 changed files with 1000 additions and 27 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -48,8 +48,10 @@ import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.ui.ModelMap;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils.MethodFilter;
import org.springframework.validation.beanvalidation.MethodValidator;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
@@ -86,6 +88,7 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite;
import org.springframework.web.method.support.HandlerMethodValidator;
import org.springframework.web.method.support.InvocableHandlerMethod;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.ModelAndView;
@@ -128,6 +131,9 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
(!AnnotatedElementUtils.hasAnnotation(method, RequestMapping.class) &&
AnnotatedElementUtils.hasAnnotation(method, ModelAttribute.class));
private final static boolean BEAN_VALIDATION_PRESENT =
ClassUtils.isPresent("jakarta.validation.Validator", HandlerMethod.class.getClassLoader());
@Nullable
private List<HandlerMethodArgumentResolver> customArgumentResolvers;
@@ -156,6 +162,9 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
@Nullable
private WebBindingInitializer webBindingInitializer;
@Nullable
private MethodValidator methodValidator;
private AsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor("MvcAsync");
@Nullable
@@ -559,6 +568,9 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
if (BEAN_VALIDATION_PRESENT) {
this.methodValidator = HandlerMethodValidator.from(this.webBindingInitializer, this.parameterNameDiscoverer);
}
}
private void initMessageConverters() {
@@ -855,6 +867,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
invocableMethod.setMethodValidator(this.methodValidator);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
@@ -955,7 +968,9 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
Object bean = handlerMethod.getBean();
initBinderMethods.add(createInitBinderMethod(bean, method));
}
return createDataBinderFactory(initBinderMethods);
DefaultDataBinderFactory factory = createDataBinderFactory(initBinderMethods);
factory.setMethodValidationApplicable(this.methodValidator != null && handlerMethod.shouldValidateArguments());
return factory;
}
private InvocableHandlerMethod createInitBinderMethod(Object bean, Method method) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -139,7 +139,7 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
HttpInputMessage inputMessage = new RequestPartServletServerHttpRequest(servletRequest, name);
arg = readWithMessageConverters(inputMessage, parameter, parameter.getNestedGenericParameterType());
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(request, arg, name);
WebDataBinder binder = binderFactory.createBinder(request, arg, name, parameter);
if (arg != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {

View File

@@ -134,7 +134,7 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
String name = Conventions.getVariableNameForParameter(parameter);
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name, parameter);
if (arg != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {