Expose ServerCodecConfigurer as a bean
With this commit, ServerCodecConfigurer is now exposed as a bean in order to be provided to DefaultServerWebExchange via WebHttpHandlerBuilder and HttpWebHandlerAdapter. This allows DefaultServerWebExchange to get configured codecs for reading form or multipart requests. Issue: SPR-14546
This commit is contained in:
parent
a712c19661
commit
8e272bc5b0
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.springframework.mock.http.server.reactive;
|
||||
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.web.server.ServerWebExchangeDecorator;
|
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.DefaultWebSessionManager;
|
||||
|
|
@ -35,7 +36,8 @@ public class MockServerWebExchange extends ServerWebExchangeDecorator {
|
|||
|
||||
public MockServerWebExchange(MockServerHttpRequest request) {
|
||||
super(new DefaultServerWebExchange(
|
||||
request, new MockServerHttpResponse(), new DefaultWebSessionManager()));
|
||||
request, new MockServerHttpResponse(), new DefaultWebSessionManager(),
|
||||
ServerCodecConfigurer.create()));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@ import org.springframework.http.HttpMethod;
|
|||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.InvalidMediaTypeException;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.FormHttpMessageReader;
|
||||
import org.springframework.http.codec.HttpMessageReader;
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.util.Assert;
|
||||
|
|
@ -46,6 +47,8 @@ import org.springframework.web.server.ServerWebExchange;
|
|||
import org.springframework.web.server.WebSession;
|
||||
import org.springframework.web.server.session.WebSessionManager;
|
||||
|
||||
import static org.springframework.http.MediaType.*;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ServerWebExchange}.
|
||||
*
|
||||
|
|
@ -56,8 +59,6 @@ public class DefaultServerWebExchange implements ServerWebExchange {
|
|||
|
||||
private static final List<HttpMethod> SAFE_METHODS = Arrays.asList(HttpMethod.GET, HttpMethod.HEAD);
|
||||
|
||||
private static final FormHttpMessageReader FORM_READER = new FormHttpMessageReader();
|
||||
|
||||
private static final ResolvableType FORM_DATA_VALUE_TYPE =
|
||||
ResolvableType.forClassWithGenerics(MultiValueMap.class, String.class, String.class);
|
||||
|
||||
|
|
@ -85,26 +86,35 @@ public class DefaultServerWebExchange implements ServerWebExchange {
|
|||
* Alternate constructor with a WebSessionManager parameter.
|
||||
*/
|
||||
public DefaultServerWebExchange(ServerHttpRequest request, ServerHttpResponse response,
|
||||
WebSessionManager sessionManager) {
|
||||
WebSessionManager sessionManager, ServerCodecConfigurer codecConfigurer) {
|
||||
|
||||
Assert.notNull(request, "'request' is required");
|
||||
Assert.notNull(response, "'response' is required");
|
||||
Assert.notNull(response, "'sessionManager' is required");
|
||||
Assert.notNull(response, "'formReader' is required");
|
||||
Assert.notNull(sessionManager, "'sessionManager' is required");
|
||||
Assert.notNull(codecConfigurer, "'codecConfigurer' is required");
|
||||
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
this.sessionMono = sessionManager.getSession(this).cache();
|
||||
this.formDataMono = initFormData(request);
|
||||
this.formDataMono = initFormData(request, codecConfigurer);
|
||||
this.requestParamsMono = initRequestParams(request, this.formDataMono);
|
||||
|
||||
}
|
||||
|
||||
private static Mono<MultiValueMap<String, String>> initFormData(ServerHttpRequest request) {
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Mono<MultiValueMap<String, String>> initFormData(
|
||||
ServerHttpRequest request, ServerCodecConfigurer codecConfigurer) {
|
||||
|
||||
MediaType contentType;
|
||||
try {
|
||||
contentType = request.getHeaders().getContentType();
|
||||
if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(contentType)) {
|
||||
return FORM_READER
|
||||
if (APPLICATION_FORM_URLENCODED.isCompatibleWith(contentType)) {
|
||||
return ((HttpMessageReader<MultiValueMap<String, String>>)codecConfigurer
|
||||
.getReaders()
|
||||
.stream()
|
||||
.filter(messageReader -> messageReader.canRead(FORM_DATA_VALUE_TYPE, APPLICATION_FORM_URLENCODED))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException("Could not find HttpMessageReader that supports " + APPLICATION_FORM_URLENCODED)))
|
||||
.readMono(FORM_DATA_VALUE_TYPE, request, Collections.emptyMap())
|
||||
.switchIfEmpty(EMPTY_FORM_DATA)
|
||||
.cache();
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import org.apache.commons.logging.LogFactory;
|
|||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
|
|
@ -38,6 +39,7 @@ import org.springframework.web.server.session.WebSessionManager;
|
|||
* then invokes the target {@code WebHandler}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sebastien Deleuze
|
||||
* @since 5.0
|
||||
*/
|
||||
public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHandler {
|
||||
|
|
@ -46,6 +48,8 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
|
|||
|
||||
private WebSessionManager sessionManager = new DefaultWebSessionManager();
|
||||
|
||||
private ServerCodecConfigurer codecConfigurer;
|
||||
|
||||
|
||||
public HttpWebHandlerAdapter(WebHandler delegate) {
|
||||
super(delegate);
|
||||
|
|
@ -71,6 +75,24 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
|
|||
return this.sessionManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a custom {@link ServerCodecConfigurer}. The provided instance is set on
|
||||
* each created {@link DefaultServerWebExchange}.
|
||||
* <p>By default this is set to {@link ServerCodecConfigurer#create()}.
|
||||
* @param codecConfigurer the codec configurer to use
|
||||
*/
|
||||
public void setCodecConfigurer(ServerCodecConfigurer codecConfigurer) {
|
||||
Assert.notNull(codecConfigurer, "ServerCodecConfigurer must not be null");
|
||||
this.codecConfigurer = codecConfigurer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured {@link ServerCodecConfigurer}.
|
||||
*/
|
||||
public ServerCodecConfigurer getCodecConfigurer() {
|
||||
return this.codecConfigurer != null ? this.codecConfigurer : ServerCodecConfigurer.create();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
|
|
@ -87,7 +109,7 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
|
|||
}
|
||||
|
||||
protected ServerWebExchange createExchange(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
return new DefaultServerWebExchange(request, response, this.sessionManager);
|
||||
return new DefaultServerWebExchange(request, response, this.sessionManager, getCodecConfigurer());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
|
@ -51,6 +52,7 @@ import org.springframework.web.server.session.WebSessionManager;
|
|||
* {@link #applicationContext(ApplicationContext)}, or a mix of both.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sebastien Deleuze
|
||||
* @since 5.0
|
||||
* @see HttpWebHandlerAdapter
|
||||
*/
|
||||
|
|
@ -62,6 +64,9 @@ public class WebHttpHandlerBuilder {
|
|||
/** Well-known name for the WebSessionManager in the bean factory. */
|
||||
public static final String WEB_SESSION_MANAGER_BEAN_NAME = "webSessionManager";
|
||||
|
||||
/** Well-known name for the ServerCodecConfigurer in the bean factory. */
|
||||
public static final String SERVER_CODEC_CONFIGURER_BEAN_NAME = "serverCodecConfigurer";
|
||||
|
||||
|
||||
private final WebHandler webHandler;
|
||||
|
||||
|
|
@ -71,6 +76,8 @@ public class WebHttpHandlerBuilder {
|
|||
|
||||
private WebSessionManager sessionManager;
|
||||
|
||||
private ServerCodecConfigurer codecConfigurer;
|
||||
|
||||
|
||||
/**
|
||||
* Private constructor.
|
||||
|
|
@ -102,6 +109,8 @@ public class WebHttpHandlerBuilder {
|
|||
* ordered.
|
||||
* <li>{@link WebSessionManager} [0..1] -- looked up by the name
|
||||
* {@link #WEB_SESSION_MANAGER_BEAN_NAME}.
|
||||
* <li>{@link ServerCodecConfigurer} [0..1] -- looked up by the name
|
||||
* {@link #SERVER_CODEC_CONFIGURER_BEAN_NAME}.
|
||||
* </ul>
|
||||
* @param context the application context to use for the lookup
|
||||
* @return the prepared builder
|
||||
|
|
@ -126,6 +135,14 @@ public class WebHttpHandlerBuilder {
|
|||
// Fall back on default
|
||||
}
|
||||
|
||||
try {
|
||||
builder.codecConfigurer(
|
||||
context.getBean(SERVER_CODEC_CONFIGURER_BEAN_NAME, ServerCodecConfigurer.class));
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// Fall back on default
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
|
@ -184,6 +201,16 @@ public class WebHttpHandlerBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the {@link ServerCodecConfigurer} to set on the
|
||||
* {@link ServerWebExchange WebServerExchange}.
|
||||
* @param codecConfigurer the codec configurer
|
||||
*/
|
||||
public WebHttpHandlerBuilder codecConfigurer(ServerCodecConfigurer codecConfigurer) {
|
||||
this.codecConfigurer = codecConfigurer;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build the {@link HttpHandler}.
|
||||
|
|
@ -199,6 +226,9 @@ public class WebHttpHandlerBuilder {
|
|||
if (this.sessionManager != null) {
|
||||
adapted.setSessionManager(this.sessionManager);
|
||||
}
|
||||
if (this.codecConfigurer != null) {
|
||||
adapted.setCodecConfigurer(this.codecConfigurer);
|
||||
}
|
||||
|
||||
return adapted;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.springframework.mock.http.server.reactive.test;
|
||||
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.web.server.ServerWebExchangeDecorator;
|
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.DefaultWebSessionManager;
|
||||
|
|
@ -35,7 +36,7 @@ public class MockServerWebExchange extends ServerWebExchangeDecorator {
|
|||
|
||||
public MockServerWebExchange(MockServerHttpRequest request) {
|
||||
super(new DefaultServerWebExchange(
|
||||
request, new MockServerHttpResponse(), new DefaultWebSessionManager()));
|
||||
request, new MockServerHttpResponse(), new DefaultWebSessionManager(), ServerCodecConfigurer.create()));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import java.util.List;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
|
@ -59,7 +60,7 @@ public class DefaultWebSessionManagerTests {
|
|||
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/path").build();
|
||||
MockServerHttpResponse response = new MockServerHttpResponse();
|
||||
this.exchange = new DefaultServerWebExchange(request, response, this.manager);
|
||||
this.exchange = new DefaultServerWebExchange(request, response, this.manager, ServerCodecConfigurer.create());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -81,8 +81,6 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware {
|
|||
|
||||
private PathMatchConfigurer pathMatchConfigurer;
|
||||
|
||||
private ServerCodecConfigurer messageCodecsConfigurer;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
|
||||
|
|
@ -242,7 +240,7 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware {
|
|||
@Bean
|
||||
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
|
||||
RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
|
||||
adapter.setMessageCodecConfigurer(getMessageCodecsConfigurer());
|
||||
adapter.setMessageCodecConfigurer(serverCodecConfigurer());
|
||||
adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());
|
||||
adapter.setReactiveAdapterRegistry(webFluxAdapterRegistry());
|
||||
|
||||
|
|
@ -267,16 +265,15 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware {
|
|||
}
|
||||
|
||||
/**
|
||||
* Main method to access the configurer for HTTP message readers and writers.
|
||||
* Return the configurer for HTTP message readers and writers.
|
||||
* <p>Use {@link #configureHttpMessageCodecs(ServerCodecConfigurer)} to
|
||||
* configure the readers and writers.
|
||||
*/
|
||||
protected final ServerCodecConfigurer getMessageCodecsConfigurer() {
|
||||
if (this.messageCodecsConfigurer == null) {
|
||||
this.messageCodecsConfigurer = ServerCodecConfigurer.create();
|
||||
configureHttpMessageCodecs(this.getMessageCodecsConfigurer());
|
||||
}
|
||||
return this.messageCodecsConfigurer;
|
||||
@Bean
|
||||
public ServerCodecConfigurer serverCodecConfigurer() {
|
||||
ServerCodecConfigurer serverCodecConfigurer = ServerCodecConfigurer.create();
|
||||
configureHttpMessageCodecs(serverCodecConfigurer);
|
||||
return serverCodecConfigurer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -372,13 +369,13 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware {
|
|||
|
||||
@Bean
|
||||
public ResponseEntityResultHandler responseEntityResultHandler() {
|
||||
return new ResponseEntityResultHandler(getMessageCodecsConfigurer().getWriters(),
|
||||
return new ResponseEntityResultHandler(serverCodecConfigurer().getWriters(),
|
||||
webFluxContentTypeResolver(), webFluxAdapterRegistry());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ResponseBodyResultHandler responseBodyResultHandler() {
|
||||
return new ResponseBodyResultHandler(getMessageCodecsConfigurer().getWriters(),
|
||||
return new ResponseBodyResultHandler(serverCodecConfigurer().getWriters(),
|
||||
webFluxContentTypeResolver(), webFluxAdapterRegistry());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -125,13 +125,13 @@ public class DispatcherHandlerIntegrationTests extends AbstractHttpHandlerIntegr
|
|||
new HandlerStrategies() {
|
||||
@Override
|
||||
public Supplier<Stream<HttpMessageReader<?>>> messageReaders() {
|
||||
return () -> getMessageCodecsConfigurer().getReaders().stream()
|
||||
return () -> serverCodecConfigurer().getReaders().stream()
|
||||
.map(reader -> (HttpMessageReader<?>) reader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<Stream<HttpMessageWriter<?>>> messageWriters() {
|
||||
return () -> getMessageCodecsConfigurer().getWriters().stream()
|
||||
return () -> serverCodecConfigurer().getWriters().stream()
|
||||
.map(writer -> (HttpMessageWriter<?>) writer);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import org.springframework.core.MethodParameter;
|
|||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.annotation.SynthesizingMethodParameter;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse;
|
||||
|
|
@ -81,7 +82,7 @@ public class SessionAttributeMethodArgumentResolverTests {
|
|||
WebSessionManager sessionManager = new MockWebSessionManager(this.session);
|
||||
|
||||
ServerHttpRequest request = MockServerHttpRequest.get("/").build();
|
||||
this.exchange = new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
|
||||
this.exchange = new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager, ServerCodecConfigurer.create());
|
||||
|
||||
this.handleMethod = ReflectionUtils.findMethod(getClass(), "handleWithSessionAttribute", (Class<?>[]) null);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue