Add global Validator bean to WebReactiveConfiguration

master
Rossen Stoyanchev 8 years ago
parent 2f8baac4e0
commit 551b7cd60e
  1. 1
      spring-web-reactive/build.gradle
  2. 58
      spring-web-reactive/src/main/java/org/springframework/web/reactive/config/WebReactiveConfiguration.java
  3. 23
      spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java
  4. 8
      spring-web-reactive/src/test/java/org/springframework/web/reactive/config/WebReactiveConfigurationTests.java

@ -111,6 +111,7 @@ dependencies {
optional "org.eclipse.jetty:jetty-servlet:${jettyVersion}" optional "org.eclipse.jetty:jetty-servlet:${jettyVersion}"
optional("org.freemarker:freemarker:2.3.23") optional("org.freemarker:freemarker:2.3.23")
optional("com.fasterxml:aalto-xml:1.0.0") optional("com.fasterxml:aalto-xml:1.0.0")
optional("javax.validation:validation-api:1.0.0.GA")
provided "javax.servlet:javax.servlet-api:3.1.0" provided "javax.servlet:javax.servlet-api:3.1.0"

@ -22,6 +22,8 @@ import java.util.Map;
import reactor.core.converter.DependencyUtils; import reactor.core.converter.DependencyUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -48,6 +50,8 @@ import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
import org.springframework.http.converter.reactive.HttpMessageConverter; import org.springframework.http.converter.reactive.HttpMessageConverter;
import org.springframework.http.converter.reactive.ResourceHttpMessageConverter; import org.springframework.http.converter.reactive.ResourceHttpMessageConverter;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import org.springframework.web.reactive.accept.RequestedContentTypeResolver; import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder; import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder;
import org.springframework.web.reactive.result.SimpleHandlerAdapter; import org.springframework.web.reactive.result.SimpleHandlerAdapter;
@ -185,6 +189,7 @@ public class WebReactiveConfiguration implements ApplicationContextAware {
adapter.setMessageConverters(getMessageConverters()); adapter.setMessageConverters(getMessageConverters());
adapter.setConversionService(mvcConversionService()); adapter.setConversionService(mvcConversionService());
adapter.setValidator(mvcValidator());
return adapter; return adapter;
} }
@ -284,6 +289,46 @@ public class WebReactiveConfiguration implements ApplicationContextAware {
} }
} }
/**
* Return a global {@link Validator} instance for example for validating
* {@code @RequestBody} method arguments.
* <p>Delegates to {@link #getValidator()} first. If that returns {@code null}
* checks the classpath for the presence of a JSR-303 implementations
* before creating a {@code OptionalValidatorFactoryBean}. If a JSR-303
* implementation is not available, a "no-op" {@link Validator} is returned.
*/
@Bean
public Validator mvcValidator() {
Validator validator = getValidator();
if (validator == null) {
if (ClassUtils.isPresent("javax.validation.Validator", getClass().getClassLoader())) {
Class<?> clazz;
try {
String name = "org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean";
clazz = ClassUtils.forName(name, classLoader);
}
catch (ClassNotFoundException ex) {
throw new BeanInitializationException("Could not find default validator class", ex);
}
catch (LinkageError ex) {
throw new BeanInitializationException("Could not load default validator class", ex);
}
validator = (Validator) BeanUtils.instantiate(clazz);
}
else {
validator = new NoOpValidator();
}
}
return validator;
}
/**
* Override this method to provide a custom {@link Validator}.
*/
protected Validator getValidator() {
return null;
}
@Bean @Bean
public SimpleHandlerAdapter simpleHandlerAdapter() { public SimpleHandlerAdapter simpleHandlerAdapter() {
return new SimpleHandlerAdapter(); return new SimpleHandlerAdapter();
@ -317,4 +362,17 @@ public class WebReactiveConfiguration implements ApplicationContextAware {
protected void configureViewResolvers(ViewResolverRegistry registry) { protected void configureViewResolvers(ViewResolverRegistry registry) {
} }
private static final class NoOpValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return false;
}
@Override
public void validate(Object target, Errors errors) {
}
}
} }

@ -39,7 +39,7 @@ import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
import org.springframework.http.converter.reactive.HttpMessageConverter; import org.springframework.http.converter.reactive.HttpMessageConverter;
import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.ModelMap; import org.springframework.ui.ModelMap;
import org.springframework.util.ObjectUtils; import org.springframework.validation.Validator;
import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.annotation.ExceptionHandlerMethodResolver; import org.springframework.web.method.annotation.ExceptionHandlerMethodResolver;
import org.springframework.web.reactive.HandlerAdapter; import org.springframework.web.reactive.HandlerAdapter;
@ -67,6 +67,8 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
private ConversionService conversionService = new DefaultFormattingConversionService(); private ConversionService conversionService = new DefaultFormattingConversionService();
private Validator validator;
private ConfigurableBeanFactory beanFactory; private ConfigurableBeanFactory beanFactory;
private final Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache = new ConcurrentHashMap<>(64); private final Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache = new ConcurrentHashMap<>(64);
@ -141,6 +143,23 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
return this.conversionService; return this.conversionService;
} }
/**
* Configure a Validator for validation of controller method arguments such
* as {@code @RequestBody}.
*
* TODO: this may be replaced by DataBinder
*/
public void setValidator(Validator validator) {
this.validator = validator;
}
/**
* Return the configured Validator.
*/
public Validator getValidator() {
return this.validator;
}
/** /**
* A {@link ConfigurableBeanFactory} is expected for resolving expressions * A {@link ConfigurableBeanFactory} is expected for resolving expressions
* in method argument default values. * in method argument default values.
@ -173,7 +192,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
resolvers.add(new RequestParamMapMethodArgumentResolver()); resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver(cs, getBeanFactory())); resolvers.add(new PathVariableMethodArgumentResolver(cs, getBeanFactory()));
resolvers.add(new PathVariableMapMethodArgumentResolver()); resolvers.add(new PathVariableMapMethodArgumentResolver());
resolvers.add(new RequestBodyArgumentResolver(getMessageConverters(), cs)); resolvers.add(new RequestBodyArgumentResolver(getMessageConverters(), cs, getValidator()));
resolvers.add(new RequestHeaderMethodArgumentResolver(cs, getBeanFactory())); resolvers.add(new RequestHeaderMethodArgumentResolver(cs, getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver()); resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new CookieValueMethodArgumentResolver(cs, getBeanFactory())); resolvers.add(new CookieValueMethodArgumentResolver(cs, getBeanFactory()));

@ -20,7 +20,6 @@ import java.nio.ByteBuffer;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlRootElement;
import org.junit.Before; import org.junit.Before;
@ -50,6 +49,8 @@ import org.springframework.http.server.reactive.MockServerHttpRequest;
import org.springframework.http.server.reactive.MockServerHttpResponse; import org.springframework.http.server.reactive.MockServerHttpResponse;
import org.springframework.util.MimeType; import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils; import org.springframework.util.MimeTypeUtils;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean;
import org.springframework.web.reactive.accept.RequestedContentTypeResolver; import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping; import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
@ -148,6 +149,11 @@ public class WebReactiveConfigurationTests {
name = "mvcConversionService"; name = "mvcConversionService";
ConversionService service = context.getBean(name, ConversionService.class); ConversionService service = context.getBean(name, ConversionService.class);
assertSame(service, adapter.getConversionService()); assertSame(service, adapter.getConversionService());
name = "mvcValidator";
Validator validator = context.getBean(name, Validator.class);
assertSame(validator, adapter.getValidator());
assertEquals(OptionalValidatorFactoryBean.class, validator.getClass());
} }
@Test @Test

Loading…
Cancel
Save