Split HttpMessageConverter into ~Reader and ~Writer
This commit is contained in:
@@ -25,6 +25,7 @@ import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.reactive.result.view.HttpMessageWriterView;
|
||||
import org.springframework.web.reactive.result.view.UrlBasedViewResolver;
|
||||
import org.springframework.web.reactive.result.view.View;
|
||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
@@ -97,9 +98,9 @@ public class ViewResolverRegistry {
|
||||
/**
|
||||
* Set default views associated with any view name and selected based on the
|
||||
* best match for the requested content type.
|
||||
* <p>Use {@link org.springframework.web.reactive.result.view.HttpMessageConverterView
|
||||
* HttpMessageConverterView} to adapt and use any existing
|
||||
* {@code HttpMessageConverter} (e.g. JSON, XML) as a {@code View}.
|
||||
* <p>Use {@link HttpMessageWriterView
|
||||
* HttpMessageWriterView} to adapt and use any existing
|
||||
* {@code HttpMessageWriter} (e.g. JSON, XML) as a {@code View}.
|
||||
*/
|
||||
public void defaultViews(View... defaultViews) {
|
||||
this.defaultViews.addAll(Arrays.asList(defaultViews));
|
||||
|
||||
@@ -28,8 +28,8 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.codec.ByteBufferDecoder;
|
||||
import org.springframework.core.codec.ByteBufferEncoder;
|
||||
import org.springframework.core.codec.Decoder;
|
||||
import org.springframework.core.codec.Encoder;
|
||||
import org.springframework.core.codec.ResourceDecoder;
|
||||
import org.springframework.core.codec.StringDecoder;
|
||||
import org.springframework.core.codec.StringEncoder;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
@@ -45,9 +45,11 @@ import org.springframework.http.codec.json.JacksonJsonDecoder;
|
||||
import org.springframework.http.codec.json.JacksonJsonEncoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2Decoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2Encoder;
|
||||
import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.ResourceHttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.DecoderHttpMessageReader;
|
||||
import org.springframework.http.converter.reactive.EncoderHttpMessageWriter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
import org.springframework.http.converter.reactive.HttpMessageWriter;
|
||||
import org.springframework.http.converter.reactive.ResourceHttpMessageWriter;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.Validator;
|
||||
@@ -87,7 +89,9 @@ public class WebReactiveConfiguration implements ApplicationContextAware {
|
||||
|
||||
private PathMatchConfigurer pathMatchConfigurer;
|
||||
|
||||
private List<HttpMessageConverter<?>> messageConverters;
|
||||
private List<HttpMessageReader<?>> messageReaders;
|
||||
|
||||
private List<HttpMessageWriter<?>> messageWriters;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@@ -189,7 +193,7 @@ public class WebReactiveConfiguration implements ApplicationContextAware {
|
||||
adapter.setCustomArgumentResolvers(resolvers);
|
||||
}
|
||||
|
||||
adapter.setMessageConverters(getMessageConverters());
|
||||
adapter.setMessageReaders(getMessageReaders());
|
||||
adapter.setConversionService(mvcConversionService());
|
||||
adapter.setValidator(mvcValidator());
|
||||
|
||||
@@ -210,65 +214,54 @@ public class WebReactiveConfiguration implements ApplicationContextAware {
|
||||
}
|
||||
|
||||
/**
|
||||
* Main method to access message converters to use for decoding
|
||||
* controller method arguments and encoding return values.
|
||||
* <p>Use {@link #configureMessageConverters} to configure the list or
|
||||
* {@link #extendMessageConverters} to add in addition to the default ones.
|
||||
* Main method to access message readers to use for decoding
|
||||
* controller method arguments with.
|
||||
* <p>Use {@link #configureMessageReaders} to configure the list or
|
||||
* {@link #extendMessageReaders} to add in addition to the default ones.
|
||||
*/
|
||||
protected final List<HttpMessageConverter<?>> getMessageConverters() {
|
||||
if (this.messageConverters == null) {
|
||||
this.messageConverters = new ArrayList<>();
|
||||
configureMessageConverters(this.messageConverters);
|
||||
if (this.messageConverters.isEmpty()) {
|
||||
addDefaultHttpMessageConverters(this.messageConverters);
|
||||
protected final List<HttpMessageReader<?>> getMessageReaders() {
|
||||
if (this.messageReaders == null) {
|
||||
this.messageReaders = new ArrayList<>();
|
||||
configureMessageReaders(this.messageReaders);
|
||||
if (this.messageReaders.isEmpty()) {
|
||||
addDefaultHttpMessageReaders(this.messageReaders);
|
||||
}
|
||||
extendMessageConverters(this.messageConverters);
|
||||
extendMessageReaders(this.messageReaders);
|
||||
}
|
||||
return this.messageConverters;
|
||||
return this.messageReaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to configure the message converters to use for decoding
|
||||
* controller method arguments and encoding return values.
|
||||
* <p>If no converters are specified, default will be added via
|
||||
* {@link #addDefaultHttpMessageConverters}.
|
||||
* @param converters a list to add converters to, initially an empty
|
||||
* Override to configure the message readers to use for decoding
|
||||
* controller method arguments.
|
||||
* <p>If no message readres are specified, default will be added via
|
||||
* {@link #addDefaultHttpMessageReaders}.
|
||||
* @param messageReaders a list to add message readers to, initially an empty
|
||||
*/
|
||||
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
protected void configureMessageReaders(List<HttpMessageReader<?>> messageReaders) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds default converters that sub-classes can call from
|
||||
* {@link #configureMessageConverters(List)}.
|
||||
* {@link #configureMessageReaders(List)}.
|
||||
*/
|
||||
protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
List<Encoder<?>> sseDataEncoders = new ArrayList<>();
|
||||
converters.add(converter(new ByteBufferEncoder(), new ByteBufferDecoder()));
|
||||
converters.add(converter(new StringEncoder(), new StringDecoder()));
|
||||
converters.add(new ResourceHttpMessageConverter());
|
||||
protected final void addDefaultHttpMessageReaders(List<HttpMessageReader<?>> readers) {
|
||||
readers.add(new DecoderHttpMessageReader<>(new ByteBufferDecoder()));
|
||||
readers.add(new DecoderHttpMessageReader<>(new StringDecoder()));
|
||||
readers.add(new DecoderHttpMessageReader<>(new ResourceDecoder()));
|
||||
if (jaxb2Present) {
|
||||
converters.add(converter(new Jaxb2Encoder(), new Jaxb2Decoder()));
|
||||
readers.add(new DecoderHttpMessageReader<>(new Jaxb2Decoder()));
|
||||
}
|
||||
if (jackson2Present) {
|
||||
JacksonJsonEncoder jacksonEncoder = new JacksonJsonEncoder();
|
||||
JacksonJsonDecoder jacksonDecoder = new JacksonJsonDecoder();
|
||||
converters.add(converter(jacksonEncoder, jacksonDecoder));
|
||||
sseDataEncoders.add(jacksonEncoder);
|
||||
} else {
|
||||
|
||||
readers.add(new DecoderHttpMessageReader<>(new JacksonJsonDecoder()));
|
||||
}
|
||||
converters.add(converter(new SseEventEncoder(sseDataEncoders), null));
|
||||
}
|
||||
|
||||
private static <T> HttpMessageConverter<T> converter(Encoder<T> encoder, Decoder<T> decoder) {
|
||||
return new CodecHttpMessageConverter<>(encoder, decoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this to modify the list of converters after it has been
|
||||
* Override this to modify the list of message readers after it has been
|
||||
* configured, for example to add some in addition to the default ones.
|
||||
*/
|
||||
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
protected void extendMessageReaders(List<HttpMessageReader<?>> messageReaders) {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -345,16 +338,67 @@ public class WebReactiveConfiguration implements ApplicationContextAware {
|
||||
|
||||
@Bean
|
||||
public ResponseEntityResultHandler responseEntityResultHandler() {
|
||||
return new ResponseEntityResultHandler(getMessageConverters(), mvcConversionService(),
|
||||
return new ResponseEntityResultHandler(getMessageWriters(), mvcConversionService(),
|
||||
mvcContentTypeResolver());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ResponseBodyResultHandler responseBodyResultHandler() {
|
||||
return new ResponseBodyResultHandler(getMessageConverters(), mvcConversionService(),
|
||||
return new ResponseBodyResultHandler(getMessageWriters(), mvcConversionService(),
|
||||
mvcContentTypeResolver());
|
||||
}
|
||||
|
||||
/**
|
||||
* Main method to access message writers to use for encoding return values.
|
||||
* <p>Use {@link #configureMessageWriters(List)} to configure the list or
|
||||
* {@link #extendMessageWriters(List)} to add in addition to the default ones.
|
||||
*/
|
||||
protected final List<HttpMessageWriter<?>> getMessageWriters() {
|
||||
if (this.messageWriters == null) {
|
||||
this.messageWriters = new ArrayList<>();
|
||||
configureMessageWriters(this.messageWriters);
|
||||
if (this.messageWriters.isEmpty()) {
|
||||
addDefaultHttpMessageWriters(this.messageWriters);
|
||||
}
|
||||
extendMessageWriters(this.messageWriters);
|
||||
}
|
||||
return this.messageWriters;
|
||||
}
|
||||
/**
|
||||
* Override to configure the message writers to use for encoding
|
||||
* return values.
|
||||
* <p>If no message readers are specified, default will be added via
|
||||
* {@link #addDefaultHttpMessageWriters}.
|
||||
* @param messageWriters a list to add message writers to, initially an empty
|
||||
*/
|
||||
protected void configureMessageWriters(List<HttpMessageWriter<?>> messageWriters) {
|
||||
}
|
||||
/**
|
||||
* Adds default converters that sub-classes can call from
|
||||
* {@link #configureMessageWriters(List)}.
|
||||
*/
|
||||
protected final void addDefaultHttpMessageWriters(List<HttpMessageWriter<?>> writers) {
|
||||
List<Encoder<?>> sseDataEncoders = new ArrayList<>();
|
||||
writers.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
|
||||
writers.add(new EncoderHttpMessageWriter<>(new StringEncoder()));
|
||||
writers.add(new ResourceHttpMessageWriter());
|
||||
if (jaxb2Present) {
|
||||
writers.add(new EncoderHttpMessageWriter<>(new Jaxb2Encoder()));
|
||||
}
|
||||
if (jackson2Present) {
|
||||
JacksonJsonEncoder jacksonEncoder = new JacksonJsonEncoder();
|
||||
writers.add(new EncoderHttpMessageWriter<>(jacksonEncoder));
|
||||
sseDataEncoders.add(jacksonEncoder);
|
||||
}
|
||||
writers.add(new EncoderHttpMessageWriter<>(new SseEventEncoder(sseDataEncoders)));
|
||||
}
|
||||
/**
|
||||
* Override this to modify the list of message writers after it has been
|
||||
* configured, for example to add some in addition to the default ones.
|
||||
*/
|
||||
protected void extendMessageWriters(List<HttpMessageWriter<?>> messageWriters) {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ViewResolutionResultHandler viewResolutionResultHandler() {
|
||||
ViewResolverRegistry registry = new ViewResolverRegistry(this.applicationContext);
|
||||
|
||||
@@ -30,7 +30,7 @@ import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -45,7 +45,7 @@ import org.springframework.web.server.UnsupportedMediaTypeStatusException;
|
||||
|
||||
/**
|
||||
* Abstract base class for argument resolvers that resolve method arguments
|
||||
* by reading the request body with an {@link HttpMessageConverter}.
|
||||
* by reading the request body with an {@link HttpMessageReader}.
|
||||
*
|
||||
* <p>Applies validation if the method argument is annotated with
|
||||
* {@code @javax.validation.Valid} or
|
||||
@@ -55,14 +55,14 @@ import org.springframework.web.server.UnsupportedMediaTypeStatusException;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class AbstractMessageConverterArgumentResolver {
|
||||
public abstract class AbstractMessageReaderArgumentResolver {
|
||||
|
||||
private static final TypeDescriptor MONO_TYPE = TypeDescriptor.valueOf(Mono.class);
|
||||
|
||||
private static final TypeDescriptor FLUX_TYPE = TypeDescriptor.valueOf(Flux.class);
|
||||
|
||||
|
||||
private final List<HttpMessageConverter<?>> messageConverters;
|
||||
private final List<HttpMessageReader<?>> messageReaders;
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
@@ -73,19 +73,19 @@ public abstract class AbstractMessageConverterArgumentResolver {
|
||||
|
||||
/**
|
||||
* Constructor with message converters and a ConversionService.
|
||||
* @param converters converters for reading the request body with
|
||||
* @param messageReaders readers to convert from the request body
|
||||
* @param service for converting to other reactive types from Flux and Mono
|
||||
* @param validator validator to validate decoded objects with
|
||||
*/
|
||||
protected AbstractMessageConverterArgumentResolver(List<HttpMessageConverter<?>> converters,
|
||||
protected AbstractMessageReaderArgumentResolver(List<HttpMessageReader<?>> messageReaders,
|
||||
ConversionService service, Validator validator) {
|
||||
|
||||
Assert.notEmpty(converters, "At least one message converter is required.");
|
||||
Assert.notEmpty(messageReaders, "At least one message reader is required.");
|
||||
Assert.notNull(service, "'conversionService' is required.");
|
||||
this.messageConverters = converters;
|
||||
this.messageReaders = messageReaders;
|
||||
this.conversionService = service;
|
||||
this.validator = validator;
|
||||
this.supportedMediaTypes = converters.stream()
|
||||
this.supportedMediaTypes = messageReaders.stream()
|
||||
.flatMap(converter -> converter.getReadableMediaTypes().stream())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
@@ -94,8 +94,8 @@ public abstract class AbstractMessageConverterArgumentResolver {
|
||||
/**
|
||||
* Return the configured message converters.
|
||||
*/
|
||||
public List<HttpMessageConverter<?>> getMessageConverters() {
|
||||
return this.messageConverters;
|
||||
public List<HttpMessageReader<?>> getMessageReaders() {
|
||||
return this.messageReaders;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,10 +124,10 @@ public abstract class AbstractMessageConverterArgumentResolver {
|
||||
mediaType = MediaType.APPLICATION_OCTET_STREAM;
|
||||
}
|
||||
|
||||
for (HttpMessageConverter<?> converter : getMessageConverters()) {
|
||||
if (converter.canRead(elementType, mediaType)) {
|
||||
for (HttpMessageReader<?> reader : getMessageReaders()) {
|
||||
if (reader.canRead(elementType, mediaType)) {
|
||||
if (convertFromFlux) {
|
||||
Flux<?> flux = converter.read(elementType, request)
|
||||
Flux<?> flux = reader.read(elementType, request)
|
||||
.onErrorResumeWith(ex -> Flux.error(getReadError(ex, bodyParameter)));
|
||||
if (checkRequired(bodyParameter, isBodyRequired)) {
|
||||
flux = flux.switchIfEmpty(Flux.error(getRequiredBodyError(bodyParameter)));
|
||||
@@ -138,7 +138,7 @@ public abstract class AbstractMessageConverterArgumentResolver {
|
||||
return Mono.just(getConversionService().convert(flux, FLUX_TYPE, typeDescriptor));
|
||||
}
|
||||
else {
|
||||
Mono<?> mono = converter.readMono(elementType, request)
|
||||
Mono<?> mono = reader.readMono(elementType, request)
|
||||
.otherwise(ex -> Mono.error(getReadError(ex, bodyParameter)));
|
||||
if (checkRequired(bodyParameter, isBodyRequired)) {
|
||||
mono = mono.otherwiseIfEmpty(Mono.error(getRequiredBodyError(bodyParameter)));
|
||||
@@ -27,7 +27,7 @@ import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageWriter;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
|
||||
@@ -37,43 +37,43 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* Abstract base class for result handlers that handle return values by writing
|
||||
* to the response with {@link HttpMessageConverter}.
|
||||
* to the response with {@link HttpMessageWriter}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class AbstractMessageConverterResultHandler extends ContentNegotiatingResultHandlerSupport {
|
||||
public abstract class AbstractMessageWriterResultHandler extends ContentNegotiatingResultHandlerSupport {
|
||||
|
||||
protected static final TypeDescriptor MONO_TYPE = TypeDescriptor.valueOf(Mono.class);
|
||||
|
||||
protected static final TypeDescriptor FLUX_TYPE = TypeDescriptor.valueOf(Flux.class);
|
||||
|
||||
|
||||
private final List<HttpMessageConverter<?>> messageConverters;
|
||||
private final List<HttpMessageWriter<?>> messageWriters;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor with message converters, a {@code ConversionService}, and a
|
||||
* {@code RequestedContentTypeResolver}.
|
||||
*
|
||||
* @param converters converters for writing the response body with
|
||||
* @param messageWriters for serializing Objects to the response body stream
|
||||
* @param conversionService for converting other reactive types (e.g.
|
||||
* rx.Observable, rx.Single, etc.) to Flux or Mono
|
||||
* @param contentTypeResolver for resolving the requested content type
|
||||
*/
|
||||
protected AbstractMessageConverterResultHandler(List<HttpMessageConverter<?>> converters,
|
||||
protected AbstractMessageWriterResultHandler(List<HttpMessageWriter<?>> messageWriters,
|
||||
ConversionService conversionService, RequestedContentTypeResolver contentTypeResolver) {
|
||||
|
||||
super(conversionService, contentTypeResolver);
|
||||
Assert.notEmpty(converters, "At least one message converter is required.");
|
||||
this.messageConverters = converters;
|
||||
Assert.notEmpty(messageWriters, "At least one message writer is required.");
|
||||
this.messageWriters = messageWriters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured message converters.
|
||||
*/
|
||||
public List<HttpMessageConverter<?>> getMessageConverters() {
|
||||
return this.messageConverters;
|
||||
public List<HttpMessageWriter<?>> getMessageWriters() {
|
||||
return this.messageWriters;
|
||||
}
|
||||
|
||||
|
||||
@@ -118,10 +118,10 @@ public abstract class AbstractMessageConverterResultHandler extends ContentNegot
|
||||
MediaType bestMediaType = selectMediaType(exchange, producibleTypes);
|
||||
|
||||
if (bestMediaType != null) {
|
||||
for (HttpMessageConverter<?> converter : getMessageConverters()) {
|
||||
if (converter.canWrite(elementType, bestMediaType)) {
|
||||
for (HttpMessageWriter<?> messageWriter : getMessageWriters()) {
|
||||
if (messageWriter.canWrite(elementType, bestMediaType)) {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
return converter.write((Publisher) publisher, elementType, bestMediaType, response);
|
||||
return messageWriter.write((Publisher) publisher, elementType, bestMediaType, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,7 +130,7 @@ public abstract class AbstractMessageConverterResultHandler extends ContentNegot
|
||||
}
|
||||
|
||||
private List<MediaType> getProducibleMediaTypes(ResolvableType elementType) {
|
||||
return getMessageConverters().stream()
|
||||
return getMessageWriters().stream()
|
||||
.filter(converter -> converter.canWrite(elementType, null))
|
||||
.flatMap(converter -> converter.getWritableMediaTypes().stream())
|
||||
.collect(Collectors.toList());
|
||||
@@ -25,7 +25,7 @@ import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.validation.Validator;
|
||||
@@ -35,36 +35,36 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
/**
|
||||
* Resolves method arguments of type {@link HttpEntity} or {@link RequestEntity}
|
||||
* by reading the body of the request through a compatible
|
||||
* {@code HttpMessageConverter}.
|
||||
* {@code HttpMessageReader}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class HttpEntityArgumentResolver extends AbstractMessageConverterArgumentResolver
|
||||
public class HttpEntityArgumentResolver extends AbstractMessageReaderArgumentResolver
|
||||
implements HandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
/**
|
||||
* Constructor with message converters and a ConversionService.
|
||||
* @param converters converters for reading the request body with
|
||||
* @param messageReaders readers for de-serializing the request body with
|
||||
* @param service for converting to other reactive types from Flux and Mono
|
||||
*/
|
||||
public HttpEntityArgumentResolver(List<HttpMessageConverter<?>> converters,
|
||||
public HttpEntityArgumentResolver(List<HttpMessageReader<?>> messageReaders,
|
||||
ConversionService service) {
|
||||
|
||||
this(converters, service, null);
|
||||
this(messageReaders, service, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with message converters and a ConversionService.
|
||||
* @param converters converters for reading the request body with
|
||||
* @param messageReaders readers for de-serializing the request body with
|
||||
* @param service for converting to other reactive types from Flux and Mono
|
||||
* @param validator validator to validate decoded objects with
|
||||
*/
|
||||
public HttpEntityArgumentResolver(List<HttpMessageConverter<?>> converters,
|
||||
public HttpEntityArgumentResolver(List<HttpMessageReader<?>> messageReaders,
|
||||
ConversionService service, Validator validator) {
|
||||
|
||||
super(converters, service, validator);
|
||||
super(messageReaders, service, validator);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
@@ -32,7 +32,7 @@ import org.springframework.web.server.ServerWebInputException;
|
||||
|
||||
/**
|
||||
* Resolves method arguments annotated with {@code @RequestBody} by reading the
|
||||
* body of the request through a compatible {@code HttpMessageConverter}.
|
||||
* body of the request through a compatible {@code HttpMessageReader}.
|
||||
*
|
||||
* <p>An {@code @RequestBody} method argument is also validated if it is
|
||||
* annotated with {@code @javax.validation.Valid} or
|
||||
@@ -44,31 +44,31 @@ import org.springframework.web.server.ServerWebInputException;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class RequestBodyArgumentResolver extends AbstractMessageConverterArgumentResolver
|
||||
public class RequestBodyArgumentResolver extends AbstractMessageReaderArgumentResolver
|
||||
implements HandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
/**
|
||||
* Constructor with message converters and a ConversionService.
|
||||
* @param converters converters for reading the request body with
|
||||
* @param messageReaders readers for de-serializing the request body with
|
||||
* @param service for converting to other reactive types from Flux and Mono
|
||||
*/
|
||||
public RequestBodyArgumentResolver(List<HttpMessageConverter<?>> converters,
|
||||
public RequestBodyArgumentResolver(List<HttpMessageReader<?>> messageReaders,
|
||||
ConversionService service) {
|
||||
|
||||
this(converters, service, null);
|
||||
this(messageReaders, service, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with message converters and a ConversionService.
|
||||
* @param converters converters for reading the request body with
|
||||
* @param messageReaders readers for de-serializing the request body with
|
||||
* @param service for converting to other reactive types from Flux and Mono
|
||||
* @param validator validator to validate decoded objects with
|
||||
*/
|
||||
public RequestBodyArgumentResolver(List<HttpMessageConverter<?>> converters,
|
||||
public RequestBodyArgumentResolver(List<HttpMessageReader<?>> messageReaders,
|
||||
ConversionService service, Validator validator) {
|
||||
|
||||
super(converters, service, validator);
|
||||
super(messageReaders, service, validator);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -35,8 +35,8 @@ import org.springframework.core.codec.ByteBufferDecoder;
|
||||
import org.springframework.core.codec.StringDecoder;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.DecoderHttpMessageReader;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.validation.Validator;
|
||||
@@ -64,7 +64,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
||||
|
||||
private List<HandlerMethodArgumentResolver> argumentResolvers;
|
||||
|
||||
private final List<HttpMessageConverter<?>> messageConverters = new ArrayList<>(10);
|
||||
private final List<HttpMessageReader<?>> messageReaders = new ArrayList<>(10);
|
||||
|
||||
private ConversionService conversionService = new DefaultFormattingConversionService();
|
||||
|
||||
@@ -77,8 +77,8 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
||||
|
||||
|
||||
public RequestMappingHandlerAdapter() {
|
||||
this.messageConverters.add(new CodecHttpMessageConverter<>(new ByteBufferDecoder()));
|
||||
this.messageConverters.add(new CodecHttpMessageConverter<>(new StringDecoder()));
|
||||
this.messageReaders.add(new DecoderHttpMessageReader<>(new ByteBufferDecoder()));
|
||||
this.messageReaders.add(new DecoderHttpMessageReader<>(new StringDecoder()));
|
||||
}
|
||||
|
||||
|
||||
@@ -112,18 +112,18 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure message converters to read the request body with.
|
||||
* Configure message readers to de-serialize the request body with.
|
||||
*/
|
||||
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
|
||||
this.messageConverters.clear();
|
||||
this.messageConverters.addAll(messageConverters);
|
||||
public void setMessageReaders(List<HttpMessageReader<?>> messageReaders) {
|
||||
this.messageReaders.clear();
|
||||
this.messageReaders.addAll(messageReaders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured message converters.
|
||||
* Return the configured message readers.
|
||||
*/
|
||||
public List<HttpMessageConverter<?>> getMessageConverters() {
|
||||
return this.messageConverters;
|
||||
public List<HttpMessageReader<?>> getMessageReaders() {
|
||||
return this.messageReaders;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,7 +193,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
||||
resolvers.add(new RequestParamMapMethodArgumentResolver());
|
||||
resolvers.add(new PathVariableMethodArgumentResolver(cs, getBeanFactory()));
|
||||
resolvers.add(new PathVariableMapMethodArgumentResolver());
|
||||
resolvers.add(new RequestBodyArgumentResolver(getMessageConverters(), cs, getValidator()));
|
||||
resolvers.add(new RequestBodyArgumentResolver(getMessageReaders(), cs, getValidator()));
|
||||
resolvers.add(new RequestHeaderMethodArgumentResolver(cs, getBeanFactory()));
|
||||
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
|
||||
resolvers.add(new CookieValueMethodArgumentResolver(cs, getBeanFactory()));
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageWriter;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.reactive.HandlerResult;
|
||||
import org.springframework.web.reactive.HandlerResultHandler;
|
||||
@@ -37,7 +37,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
/**
|
||||
* {@code HandlerResultHandler} that handles return values from methods annotated
|
||||
* with {@code @ResponseBody} writing to the body of the request or response with
|
||||
* an {@link HttpMessageConverter}.
|
||||
* an {@link HttpMessageWriter}.
|
||||
*
|
||||
* <p>By default the order for this result handler is set to 100. As it detects
|
||||
* the presence of {@code @ResponseBody} it should be ordered after result
|
||||
@@ -50,7 +50,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ResponseBodyResultHandler extends AbstractMessageConverterResultHandler
|
||||
public class ResponseBodyResultHandler extends AbstractMessageWriterResultHandler
|
||||
implements HandlerResultHandler {
|
||||
|
||||
/**
|
||||
@@ -58,28 +58,28 @@ public class ResponseBodyResultHandler extends AbstractMessageConverterResultHan
|
||||
* and creating a {@link HeaderContentTypeResolver}, i.e. using Accept header
|
||||
* to determine the requested content type.
|
||||
*
|
||||
* @param converters converters for writing the response body with
|
||||
* @param messageWriters writers for serializing to the response body stream
|
||||
* @param conversionService for converting to Flux and Mono from other reactive types
|
||||
*/
|
||||
public ResponseBodyResultHandler(List<HttpMessageConverter<?>> converters,
|
||||
public ResponseBodyResultHandler(List<HttpMessageWriter<?>> messageWriters,
|
||||
ConversionService conversionService) {
|
||||
|
||||
this(converters, conversionService, new HeaderContentTypeResolver());
|
||||
this(messageWriters, conversionService, new HeaderContentTypeResolver());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with message converters, a {@code ConversionService}, and a
|
||||
* {@code RequestedContentTypeResolver}.
|
||||
*
|
||||
* @param converters converters for writing the response body with
|
||||
* @param messageWriters writers for serializing to the response body stream
|
||||
* @param conversionService for converting other reactive types (e.g.
|
||||
* rx.Observable, rx.Single, etc.) to Flux or Mono
|
||||
* @param contentTypeResolver for resolving the requested content type
|
||||
*/
|
||||
public ResponseBodyResultHandler(List<HttpMessageConverter<?>> converters,
|
||||
public ResponseBodyResultHandler(List<HttpMessageWriter<?>> messageWriters,
|
||||
ConversionService conversionService, RequestedContentTypeResolver contentTypeResolver) {
|
||||
|
||||
super(converters, conversionService, contentTypeResolver);
|
||||
super(messageWriters, conversionService, contentTypeResolver);
|
||||
setOrder(100);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageWriter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.HandlerResult;
|
||||
import org.springframework.web.reactive.HandlerResultHandler;
|
||||
@@ -44,7 +44,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ResponseEntityResultHandler extends AbstractMessageConverterResultHandler
|
||||
public class ResponseEntityResultHandler extends AbstractMessageWriterResultHandler
|
||||
implements HandlerResultHandler {
|
||||
|
||||
/**
|
||||
@@ -52,28 +52,28 @@ public class ResponseEntityResultHandler extends AbstractMessageConverterResultH
|
||||
* and creating a {@link HeaderContentTypeResolver}, i.e. using Accept header
|
||||
* to determine the requested content type.
|
||||
*
|
||||
* @param converters converters for writing the response body with
|
||||
* @param messageWriters writers for serializing to the response body stream
|
||||
* @param conversionService for converting to Flux and Mono from other reactive types
|
||||
*/
|
||||
public ResponseEntityResultHandler(List<HttpMessageConverter<?>> converters,
|
||||
public ResponseEntityResultHandler(List<HttpMessageWriter<?>> messageWriters,
|
||||
ConversionService conversionService) {
|
||||
|
||||
this(converters, conversionService, new HeaderContentTypeResolver());
|
||||
this(messageWriters, conversionService, new HeaderContentTypeResolver());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with message converters, a {@code ConversionService}, and a
|
||||
* {@code RequestedContentTypeResolver}.
|
||||
*
|
||||
* @param converters converters for writing the response body with
|
||||
* @param messageWriters writers for serializing to the response body stream
|
||||
* @param conversionService for converting other reactive types (e.g.
|
||||
* rx.Observable, rx.Single, etc.) to Flux or Mono
|
||||
* @param contentTypeResolver for resolving the requested content type
|
||||
*/
|
||||
public ResponseEntityResultHandler(List<HttpMessageConverter<?>> converters,
|
||||
public ResponseEntityResultHandler(List<HttpMessageWriter<?>> messageWriters,
|
||||
ConversionService conversionService, RequestedContentTypeResolver contentTypeResolver) {
|
||||
|
||||
super(converters, conversionService, contentTypeResolver);
|
||||
super(messageWriters, conversionService, contentTypeResolver);
|
||||
setOrder(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.Encoder;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.EncoderHttpMessageWriter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageWriter;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -37,14 +37,14 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
|
||||
/**
|
||||
* A {@link View} that delegates to an {@link HttpMessageConverter}.
|
||||
* A {@link View} that delegates to an {@link HttpMessageWriter}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class HttpMessageConverterView implements View {
|
||||
public class HttpMessageWriterView implements View {
|
||||
|
||||
private final HttpMessageConverter<?> converter;
|
||||
private final HttpMessageWriter<?> messageWriter;
|
||||
|
||||
private final Set<String> modelKeys = new HashSet<>(4);
|
||||
|
||||
@@ -52,35 +52,33 @@ public class HttpMessageConverterView implements View {
|
||||
|
||||
|
||||
/**
|
||||
* Create a {@code View} with the given {@code Encoder}.
|
||||
* Internally this creates
|
||||
* {@link CodecHttpMessageConverter#CodecHttpMessageConverter(Encoder)
|
||||
* CodecHttpMessageConverter(Encoder)}.
|
||||
* Create a {@code View} with the given {@code Encoder} wrapping it as an
|
||||
* {@link EncoderHttpMessageWriter}.
|
||||
*/
|
||||
public HttpMessageConverterView(Encoder<?> encoder) {
|
||||
this(new CodecHttpMessageConverter<>(encoder));
|
||||
public HttpMessageWriterView(Encoder<?> encoder) {
|
||||
this(new EncoderHttpMessageWriter<>(encoder));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a View that delegates to the given message converter.
|
||||
* Create a View that delegates to the given message messageWriter.
|
||||
*/
|
||||
public HttpMessageConverterView(HttpMessageConverter<?> converter) {
|
||||
Assert.notNull(converter, "'converter' is required.");
|
||||
this.converter = converter;
|
||||
this.mediaTypes = converter.getWritableMediaTypes();
|
||||
public HttpMessageWriterView(HttpMessageWriter<?> messageWriter) {
|
||||
Assert.notNull(messageWriter, "'messageWriter' is required.");
|
||||
this.messageWriter = messageWriter;
|
||||
this.mediaTypes = messageWriter.getWritableMediaTypes();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the configured message converter.
|
||||
* Return the configured message messageWriter.
|
||||
*/
|
||||
public HttpMessageConverter<?> getConverter() {
|
||||
return this.converter;
|
||||
public HttpMessageWriter<?> getMessageWriter() {
|
||||
return this.messageWriter;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default model attributes are filtered with
|
||||
* {@link HttpMessageConverter#canWrite} to find the ones that can be
|
||||
* {@link HttpMessageWriter#canWrite} to find the ones that can be
|
||||
* rendered. Use this property to further narrow the list and consider only
|
||||
* attribute(s) under specific model key(s).
|
||||
* <p>If more than one matching attribute is found, than a Map is rendered,
|
||||
@@ -109,7 +107,7 @@ public class HttpMessageConverterView implements View {
|
||||
@Override
|
||||
public Mono<Void> render(HandlerResult result, MediaType contentType, ServerWebExchange exchange) {
|
||||
Object value = extractObjectToRender(result);
|
||||
return applyConverter(value, contentType, exchange);
|
||||
return applyMessageWriter(value, contentType, exchange);
|
||||
}
|
||||
|
||||
protected Object extractObjectToRender(HandlerResult result) {
|
||||
@@ -126,13 +124,13 @@ public class HttpMessageConverterView implements View {
|
||||
else if (map.size() == 1) {
|
||||
return map.values().iterator().next();
|
||||
}
|
||||
else if (getConverter().canWrite(ResolvableType.forClass(Map.class), null)) {
|
||||
else if (getMessageWriter().canWrite(ResolvableType.forClass(Map.class), null)) {
|
||||
return map;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(
|
||||
"Multiple matching attributes found: " + map + ". " +
|
||||
"However Map rendering is not supported by " + getConverter());
|
||||
"However Map rendering is not supported by " + getMessageWriter());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,28 +143,28 @@ public class HttpMessageConverterView implements View {
|
||||
protected boolean isEligibleAttribute(String attributeName, Object attributeValue) {
|
||||
ResolvableType type = ResolvableType.forClass(attributeValue.getClass());
|
||||
if (getModelKeys().isEmpty()) {
|
||||
return getConverter().canWrite(type, null);
|
||||
return getMessageWriter().canWrite(type, null);
|
||||
}
|
||||
if (getModelKeys().contains(attributeName)) {
|
||||
if (getConverter().canWrite(type, null)) {
|
||||
if (getMessageWriter().canWrite(type, null)) {
|
||||
return true;
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
"Model object [" + attributeValue + "] retrieved via key " +
|
||||
"[" + attributeName + "] is not supported by " + getConverter());
|
||||
"[" + attributeName + "] is not supported by " + getMessageWriter());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> Mono<Void> applyConverter(Object value, MediaType contentType, ServerWebExchange exchange) {
|
||||
private <T> Mono<Void> applyMessageWriter(Object value, MediaType contentType, ServerWebExchange exchange) {
|
||||
if (value == null) {
|
||||
return Mono.empty();
|
||||
}
|
||||
Publisher<? extends T> stream = Mono.just((T) value);
|
||||
ResolvableType type = ResolvableType.forClass(value.getClass());
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
return ((HttpMessageConverter<T>) getConverter()).write(stream, type, contentType, response);
|
||||
return ((HttpMessageWriter<T>) getMessageWriter()).write(stream, type, contentType, response);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,7 +36,7 @@ import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.EncoderHttpMessageWriter;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.stereotype.Controller;
|
||||
@@ -197,7 +197,7 @@ public class DispatcherHandlerErrorTests {
|
||||
@Bean
|
||||
public ResponseBodyResultHandler resultHandler() {
|
||||
return new ResponseBodyResultHandler(
|
||||
Collections.singletonList(new CodecHttpMessageConverter<>(new StringEncoder())),
|
||||
Collections.singletonList(new EncoderHttpMessageWriter<>(new StringEncoder())),
|
||||
new DefaultConversionService());
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.junit.Test;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.codec.json.JacksonJsonEncoder;
|
||||
import org.springframework.web.context.support.StaticWebApplicationContext;
|
||||
import org.springframework.web.reactive.result.view.HttpMessageConverterView;
|
||||
import org.springframework.web.reactive.result.view.HttpMessageWriterView;
|
||||
import org.springframework.web.reactive.result.view.UrlBasedViewResolver;
|
||||
import org.springframework.web.reactive.result.view.View;
|
||||
import org.springframework.web.reactive.result.view.freemarker.FreeMarkerConfigurer;
|
||||
@@ -80,7 +80,7 @@ public class ViewResolverRegistryTests {
|
||||
|
||||
@Test
|
||||
public void defaultViews() throws Exception {
|
||||
View view = new HttpMessageConverterView(new JacksonJsonEncoder());
|
||||
View view = new HttpMessageWriterView(new JacksonJsonEncoder());
|
||||
this.registry.defaultViews(view);
|
||||
|
||||
assertEquals(1, this.registry.getDefaultViews().size());
|
||||
|
||||
@@ -43,8 +43,10 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.json.JacksonJsonEncoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2Decoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2Encoder;
|
||||
import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.DecoderHttpMessageReader;
|
||||
import org.springframework.http.converter.reactive.EncoderHttpMessageWriter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
import org.springframework.http.converter.reactive.HttpMessageWriter;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.util.MimeType;
|
||||
@@ -56,7 +58,7 @@ import org.springframework.web.reactive.result.method.annotation.RequestMappingH
|
||||
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
|
||||
import org.springframework.web.reactive.result.method.annotation.ResponseBodyResultHandler;
|
||||
import org.springframework.web.reactive.result.method.annotation.ResponseEntityResultHandler;
|
||||
import org.springframework.web.reactive.result.view.HttpMessageConverterView;
|
||||
import org.springframework.web.reactive.result.view.HttpMessageWriterView;
|
||||
import org.springframework.web.reactive.result.view.View;
|
||||
import org.springframework.web.reactive.result.view.ViewResolutionResultHandler;
|
||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
@@ -142,15 +144,15 @@ public class WebReactiveConfigurationTests {
|
||||
RequestMappingHandlerAdapter adapter = context.getBean(name, RequestMappingHandlerAdapter.class);
|
||||
assertNotNull(adapter);
|
||||
|
||||
List<HttpMessageConverter<?>> converters = adapter.getMessageConverters();
|
||||
assertEquals(6, converters.size());
|
||||
List<HttpMessageReader<?>> readers = adapter.getMessageReaders();
|
||||
assertEquals(5, readers.size());
|
||||
|
||||
assertHasConverter(converters, ByteBuffer.class, APPLICATION_OCTET_STREAM, APPLICATION_OCTET_STREAM);
|
||||
assertHasConverter(converters, String.class, TEXT_PLAIN, TEXT_PLAIN);
|
||||
assertHasConverter(converters, Resource.class, IMAGE_PNG, IMAGE_PNG);
|
||||
assertHasConverter(converters, TestBean.class, APPLICATION_XML, APPLICATION_XML);
|
||||
assertHasConverter(converters, TestBean.class, APPLICATION_JSON, APPLICATION_JSON);
|
||||
assertHasConverter(converters, TestBean.class, null, MediaType.parseMediaType("text/event-stream"));
|
||||
assertHasMessageReader(readers, ByteBuffer.class, APPLICATION_OCTET_STREAM);
|
||||
assertHasMessageReader(readers, String.class, TEXT_PLAIN);
|
||||
assertHasMessageReader(readers, Resource.class, IMAGE_PNG);
|
||||
assertHasMessageReader(readers, TestBean.class, APPLICATION_XML);
|
||||
assertHasMessageReader(readers, TestBean.class, APPLICATION_JSON);
|
||||
assertHasMessageReader(readers, TestBean.class, null);
|
||||
|
||||
name = "mvcConversionService";
|
||||
ConversionService service = context.getBean(name, ConversionService.class);
|
||||
@@ -170,11 +172,11 @@ public class WebReactiveConfigurationTests {
|
||||
RequestMappingHandlerAdapter adapter = context.getBean(name, RequestMappingHandlerAdapter.class);
|
||||
assertNotNull(adapter);
|
||||
|
||||
List<HttpMessageConverter<?>> converters = adapter.getMessageConverters();
|
||||
assertEquals(2, converters.size());
|
||||
List<HttpMessageReader<?>> messageReaders = adapter.getMessageReaders();
|
||||
assertEquals(2, messageReaders.size());
|
||||
|
||||
assertHasConverter(converters, String.class, TEXT_PLAIN, TEXT_PLAIN);
|
||||
assertHasConverter(converters, TestBean.class, APPLICATION_XML, APPLICATION_XML);
|
||||
assertHasMessageReader(messageReaders, String.class, TEXT_PLAIN);
|
||||
assertHasMessageReader(messageReaders, TestBean.class, APPLICATION_XML);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -199,15 +201,15 @@ public class WebReactiveConfigurationTests {
|
||||
|
||||
assertEquals(0, handler.getOrder());
|
||||
|
||||
List<HttpMessageConverter<?>> converters = handler.getMessageConverters();
|
||||
assertEquals(6, converters.size());
|
||||
List<HttpMessageWriter<?>> writers = handler.getMessageWriters();
|
||||
assertEquals(6, writers.size());
|
||||
|
||||
assertHasConverter(converters, ByteBuffer.class, APPLICATION_OCTET_STREAM, APPLICATION_OCTET_STREAM);
|
||||
assertHasConverter(converters, String.class, TEXT_PLAIN, TEXT_PLAIN);
|
||||
assertHasConverter(converters, Resource.class, IMAGE_PNG, IMAGE_PNG);
|
||||
assertHasConverter(converters, TestBean.class, APPLICATION_XML, APPLICATION_XML);
|
||||
assertHasConverter(converters, TestBean.class, APPLICATION_JSON, APPLICATION_JSON);
|
||||
assertHasConverter(converters, TestBean.class, null, MediaType.parseMediaType("text/event-stream"));
|
||||
assertHasMessageWriter(writers, ByteBuffer.class, APPLICATION_OCTET_STREAM);
|
||||
assertHasMessageWriter(writers, String.class, TEXT_PLAIN);
|
||||
assertHasMessageWriter(writers, Resource.class, IMAGE_PNG);
|
||||
assertHasMessageWriter(writers, TestBean.class, APPLICATION_XML);
|
||||
assertHasMessageWriter(writers, TestBean.class, APPLICATION_JSON);
|
||||
assertHasMessageWriter(writers, TestBean.class, MediaType.parseMediaType("text/event-stream"));
|
||||
|
||||
name = "mvcContentTypeResolver";
|
||||
RequestedContentTypeResolver resolver = context.getBean(name, RequestedContentTypeResolver.class);
|
||||
@@ -224,15 +226,15 @@ public class WebReactiveConfigurationTests {
|
||||
|
||||
assertEquals(100, handler.getOrder());
|
||||
|
||||
List<HttpMessageConverter<?>> converters = handler.getMessageConverters();
|
||||
assertEquals(6, converters.size());
|
||||
List<HttpMessageWriter<?>> writers = handler.getMessageWriters();
|
||||
assertEquals(6, writers.size());
|
||||
|
||||
assertHasConverter(converters, ByteBuffer.class, APPLICATION_OCTET_STREAM, APPLICATION_OCTET_STREAM);
|
||||
assertHasConverter(converters, String.class, TEXT_PLAIN, TEXT_PLAIN);
|
||||
assertHasConverter(converters, Resource.class, IMAGE_PNG, IMAGE_PNG);
|
||||
assertHasConverter(converters, TestBean.class, APPLICATION_XML, APPLICATION_XML);
|
||||
assertHasConverter(converters, TestBean.class, APPLICATION_JSON, APPLICATION_JSON);
|
||||
assertHasConverter(converters, TestBean.class, null, MediaType.parseMediaType("text/event-stream"));
|
||||
assertHasMessageWriter(writers, ByteBuffer.class, APPLICATION_OCTET_STREAM);
|
||||
assertHasMessageWriter(writers, String.class, TEXT_PLAIN);
|
||||
assertHasMessageWriter(writers, Resource.class, IMAGE_PNG);
|
||||
assertHasMessageWriter(writers, TestBean.class, APPLICATION_XML);
|
||||
assertHasMessageWriter(writers, TestBean.class, APPLICATION_JSON);
|
||||
assertHasMessageWriter(writers, TestBean.class, null);
|
||||
|
||||
name = "mvcContentTypeResolver";
|
||||
RequestedContentTypeResolver resolver = context.getBean(name, RequestedContentTypeResolver.class);
|
||||
@@ -261,12 +263,18 @@ public class WebReactiveConfigurationTests {
|
||||
}
|
||||
|
||||
|
||||
private void assertHasConverter(List<HttpMessageConverter<?>> converters, Class<?> clazz,
|
||||
MediaType readMediaType, MediaType writeMediaType) {
|
||||
private void assertHasMessageReader(List<HttpMessageReader<?>> readers, Class<?> clazz, MediaType mediaType) {
|
||||
ResolvableType type = ResolvableType.forClass(clazz);
|
||||
assertTrue(converters.stream()
|
||||
.filter(c -> (readMediaType == null || c.canRead(type, readMediaType))
|
||||
&& (writeMediaType == null || c.canWrite(type, writeMediaType)))
|
||||
assertTrue(readers.stream()
|
||||
.filter(c -> mediaType == null || c.canRead(type, mediaType))
|
||||
.findAny()
|
||||
.isPresent());
|
||||
}
|
||||
|
||||
private void assertHasMessageWriter(List<HttpMessageWriter<?>> writers, Class<?> clazz, MediaType mediaType) {
|
||||
ResolvableType type = ResolvableType.forClass(clazz);
|
||||
assertTrue(writers.stream()
|
||||
.filter(c -> mediaType == null || c.canWrite(type, mediaType))
|
||||
.findAny()
|
||||
.isPresent());
|
||||
}
|
||||
@@ -293,13 +301,23 @@ public class WebReactiveConfigurationTests {
|
||||
static class CustomMessageConverterConfig extends WebReactiveConfiguration {
|
||||
|
||||
@Override
|
||||
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
converters.add(new CodecHttpMessageConverter<>(new StringEncoder(), new StringDecoder()));
|
||||
protected void configureMessageReaders(List<HttpMessageReader<?>> messageReaders) {
|
||||
messageReaders.add(new DecoderHttpMessageReader<>(new StringDecoder()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
converters.add(new CodecHttpMessageConverter<>(new Jaxb2Encoder(), new Jaxb2Decoder()));
|
||||
protected void configureMessageWriters(List<HttpMessageWriter<?>> messageWriters) {
|
||||
messageWriters.add(new EncoderHttpMessageWriter<>(new StringEncoder()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void extendMessageReaders(List<HttpMessageReader<?>> messageReaders) {
|
||||
messageReaders.add(new DecoderHttpMessageReader<>(new Jaxb2Decoder()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void extendMessageWriters(List<HttpMessageWriter<?>> messageWriters) {
|
||||
messageWriters.add(new EncoderHttpMessageWriter<>(new Jaxb2Encoder()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,7 +327,7 @@ public class WebReactiveConfigurationTests {
|
||||
@Override
|
||||
protected void configureViewResolvers(ViewResolverRegistry registry) {
|
||||
registry.freeMarker();
|
||||
registry.defaultViews(new HttpMessageConverterView(new JacksonJsonEncoder()));
|
||||
registry.defaultViews(new HttpMessageWriterView(new JacksonJsonEncoder()));
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -45,8 +45,8 @@ import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.DecoderHttpMessageReader;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
@@ -66,7 +66,7 @@ import static org.springframework.core.ResolvableType.forClassWithGenerics;
|
||||
/**
|
||||
* Unit tests for {@link HttpEntityArgumentResolver}.When adding a test also
|
||||
* consider whether the logic under test is in a parent class, then see:
|
||||
* {@link MessageConverterArgumentResolverTests}.
|
||||
* {@link MessageReaderArgumentResolverTests}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
@@ -89,14 +89,14 @@ public class HttpEntityArgumentResolverTests {
|
||||
}
|
||||
|
||||
private HttpEntityArgumentResolver createResolver() {
|
||||
List<HttpMessageConverter<?>> converters = new ArrayList<>();
|
||||
converters.add(new CodecHttpMessageConverter<>(new StringDecoder()));
|
||||
List<HttpMessageReader<?>> readers = new ArrayList<>();
|
||||
readers.add(new DecoderHttpMessageReader<>(new StringDecoder()));
|
||||
|
||||
FormattingConversionService service = new DefaultFormattingConversionService();
|
||||
service.addConverter(new MonoToCompletableFutureConverter());
|
||||
service.addConverter(new ReactorToRxJava1Converter());
|
||||
|
||||
return new HttpEntityArgumentResolver(converters, service);
|
||||
return new HttpEntityArgumentResolver(readers, service);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -50,8 +50,8 @@ import org.springframework.format.support.FormattingConversionService;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.json.JacksonJsonDecoder;
|
||||
import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.DecoderHttpMessageReader;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.validation.Errors;
|
||||
@@ -72,12 +72,12 @@ import static org.springframework.core.ResolvableType.forClass;
|
||||
import static org.springframework.core.ResolvableType.forClassWithGenerics;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AbstractMessageConverterArgumentResolver}.
|
||||
* Unit tests for {@link AbstractMessageReaderArgumentResolver}.
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class MessageConverterArgumentResolverTests {
|
||||
public class MessageReaderArgumentResolverTests {
|
||||
|
||||
private AbstractMessageConverterArgumentResolver resolver = resolver(new JacksonJsonDecoder());
|
||||
private AbstractMessageReaderArgumentResolver resolver = resolver(new JacksonJsonDecoder());
|
||||
|
||||
private ServerWebExchange exchange;
|
||||
|
||||
@@ -276,16 +276,16 @@ public class MessageConverterArgumentResolverTests {
|
||||
}
|
||||
|
||||
@SuppressWarnings("Convert2MethodRef")
|
||||
private AbstractMessageConverterArgumentResolver resolver(Decoder<?>... decoders) {
|
||||
private AbstractMessageReaderArgumentResolver resolver(Decoder<?>... decoders) {
|
||||
|
||||
List<HttpMessageConverter<?>> converters = new ArrayList<>();
|
||||
Arrays.asList(decoders).forEach(decoder -> converters.add(new CodecHttpMessageConverter<>(decoder)));
|
||||
List<HttpMessageReader<?>> readers = new ArrayList<>();
|
||||
Arrays.asList(decoders).forEach(decoder -> readers.add(new DecoderHttpMessageReader<>(decoder)));
|
||||
|
||||
FormattingConversionService service = new DefaultFormattingConversionService();
|
||||
service.addConverter(new MonoToCompletableFutureConverter());
|
||||
service.addConverter(new ReactorToRxJava1Converter());
|
||||
|
||||
return new AbstractMessageConverterArgumentResolver(converters, service, new TestBeanValidator()) {};
|
||||
return new AbstractMessageReaderArgumentResolver(readers, service, new TestBeanValidator()) {};
|
||||
}
|
||||
|
||||
private DataBuffer dataBuffer(String body) {
|
||||
@@ -49,9 +49,9 @@ import org.springframework.core.io.buffer.support.DataBufferTestUtils;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.codec.json.JacksonJsonEncoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2Encoder;
|
||||
import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.ResourceHttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.EncoderHttpMessageWriter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageWriter;
|
||||
import org.springframework.http.converter.reactive.ResourceHttpMessageWriter;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
@@ -70,12 +70,12 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8;
|
||||
import static org.springframework.web.reactive.HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AbstractMessageConverterResultHandler}.
|
||||
* Unit tests for {@link AbstractMessageWriterResultHandler}.
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class MessageConverterResultHandlerTests {
|
||||
public class MessageWriterResultHandlerTests {
|
||||
|
||||
private AbstractMessageConverterResultHandler resultHandler;
|
||||
private AbstractMessageWriterResultHandler resultHandler;
|
||||
|
||||
private MockServerHttpResponse response = new MockServerHttpResponse();
|
||||
|
||||
@@ -131,8 +131,8 @@ public class MessageConverterResultHandlerTests {
|
||||
ByteArrayOutputStream body = new ByteArrayOutputStream();
|
||||
ResolvableType type = ResolvableType.forType(OutputStream.class);
|
||||
|
||||
HttpMessageConverter<?> converter = new CodecHttpMessageConverter<>(new ByteBufferEncoder());
|
||||
Mono<Void> mono = createResultHandler(converter).writeBody(this.exchange, body, type, returnType(type));
|
||||
HttpMessageWriter<?> writer = new EncoderHttpMessageWriter<>(new ByteBufferEncoder());
|
||||
Mono<Void> mono = createResultHandler(writer).writeBody(this.exchange, body, type, returnType(type));
|
||||
|
||||
TestSubscriber.subscribe(mono).assertError(IllegalStateException.class);
|
||||
}
|
||||
@@ -175,18 +175,18 @@ public class MessageConverterResultHandlerTests {
|
||||
return ResolvableMethod.onClass(TestController.class).returning(bodyType).resolveReturnType();
|
||||
}
|
||||
|
||||
private AbstractMessageConverterResultHandler createResultHandler(HttpMessageConverter<?>... converters) {
|
||||
List<HttpMessageConverter<?>> converterList;
|
||||
if (ObjectUtils.isEmpty(converters)) {
|
||||
converterList = new ArrayList<>();
|
||||
converterList.add(new CodecHttpMessageConverter<>(new ByteBufferEncoder()));
|
||||
converterList.add(new CodecHttpMessageConverter<>(new StringEncoder()));
|
||||
converterList.add(new ResourceHttpMessageConverter());
|
||||
converterList.add(new CodecHttpMessageConverter<>(new Jaxb2Encoder()));
|
||||
converterList.add(new CodecHttpMessageConverter<>(new JacksonJsonEncoder()));
|
||||
private AbstractMessageWriterResultHandler createResultHandler(HttpMessageWriter<?>... writers) {
|
||||
List<HttpMessageWriter<?>> writerList;
|
||||
if (ObjectUtils.isEmpty(writers)) {
|
||||
writerList = new ArrayList<>();
|
||||
writerList.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
|
||||
writerList.add(new EncoderHttpMessageWriter<>(new StringEncoder()));
|
||||
writerList.add(new ResourceHttpMessageWriter());
|
||||
writerList.add(new EncoderHttpMessageWriter<>(new Jaxb2Encoder()));
|
||||
writerList.add(new EncoderHttpMessageWriter<>(new JacksonJsonEncoder()));
|
||||
}
|
||||
else {
|
||||
converterList = Arrays.asList(converters);
|
||||
writerList = Arrays.asList(writers);
|
||||
}
|
||||
|
||||
GenericConversionService service = new GenericConversionService();
|
||||
@@ -195,7 +195,7 @@ public class MessageConverterResultHandlerTests {
|
||||
|
||||
RequestedContentTypeResolver resolver = new RequestedContentTypeResolverBuilder().build();
|
||||
|
||||
return new AbstractMessageConverterResultHandler(converterList, service, resolver) {};
|
||||
return new AbstractMessageWriterResultHandler(writerList, service, resolver) {};
|
||||
}
|
||||
|
||||
private void assertResponseBody(String responseBody) {
|
||||
@@ -44,8 +44,8 @@ import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.DecoderHttpMessageReader;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
@@ -67,7 +67,7 @@ import static org.springframework.core.ResolvableType.forClassWithGenerics;
|
||||
/**
|
||||
* Unit tests for {@link RequestBodyArgumentResolver}. When adding a test also
|
||||
* consider whether the logic under test is in a parent class, then see:
|
||||
* {@link MessageConverterArgumentResolverTests}.
|
||||
* {@link MessageReaderArgumentResolverTests}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
@@ -90,14 +90,14 @@ public class RequestBodyArgumentResolverTests {
|
||||
}
|
||||
|
||||
private RequestBodyArgumentResolver resolver() {
|
||||
List<HttpMessageConverter<?>> converters = new ArrayList<>();
|
||||
converters.add(new CodecHttpMessageConverter<>(new StringDecoder()));
|
||||
List<HttpMessageReader<?>> readers = new ArrayList<>();
|
||||
readers.add(new DecoderHttpMessageReader<>(new StringDecoder()));
|
||||
|
||||
FormattingConversionService service = new DefaultFormattingConversionService();
|
||||
service.addConverter(new MonoToCompletableFutureConverter());
|
||||
service.addConverter(new ReactorToRxJava1Converter());
|
||||
|
||||
return new RequestBodyArgumentResolver(converters, service);
|
||||
return new RequestBodyArgumentResolver(readers, service);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -36,9 +36,9 @@ import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.codec.json.JacksonJsonEncoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2Encoder;
|
||||
import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.ResourceHttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.EncoderHttpMessageWriter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageWriter;
|
||||
import org.springframework.http.converter.reactive.ResourceHttpMessageWriter;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
@@ -62,7 +62,7 @@ import static org.junit.Assert.assertEquals;
|
||||
* Unit tests for {@link ResponseBodyResultHandler}.When adding a test also
|
||||
* consider whether the logic under test is in a parent class, then see:
|
||||
* <ul>
|
||||
* <li>{@code MessageConverterResultHandlerTests},
|
||||
* <li>{@code MessageWriterResultHandlerTests},
|
||||
* <li>{@code ContentNegotiatingResultHandlerSupportTests}
|
||||
* </ul>
|
||||
*
|
||||
@@ -86,25 +86,25 @@ public class ResponseBodyResultHandlerTests {
|
||||
}
|
||||
|
||||
|
||||
private ResponseBodyResultHandler createHandler(HttpMessageConverter<?>... converters) {
|
||||
List<HttpMessageConverter<?>> converterList;
|
||||
if (ObjectUtils.isEmpty(converters)) {
|
||||
converterList = new ArrayList<>();
|
||||
converterList.add(new CodecHttpMessageConverter<>(new ByteBufferEncoder()));
|
||||
converterList.add(new CodecHttpMessageConverter<>(new StringEncoder()));
|
||||
converterList.add(new ResourceHttpMessageConverter());
|
||||
converterList.add(new CodecHttpMessageConverter<>(new Jaxb2Encoder()));
|
||||
converterList.add(new CodecHttpMessageConverter<>(new JacksonJsonEncoder()));
|
||||
private ResponseBodyResultHandler createHandler(HttpMessageWriter<?>... writers) {
|
||||
List<HttpMessageWriter<?>> writerList;
|
||||
if (ObjectUtils.isEmpty(writers)) {
|
||||
writerList = new ArrayList<>();
|
||||
writerList.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
|
||||
writerList.add(new EncoderHttpMessageWriter<>(new StringEncoder()));
|
||||
writerList.add(new ResourceHttpMessageWriter());
|
||||
writerList.add(new EncoderHttpMessageWriter<>(new Jaxb2Encoder()));
|
||||
writerList.add(new EncoderHttpMessageWriter<>(new JacksonJsonEncoder()));
|
||||
}
|
||||
else {
|
||||
converterList = Arrays.asList(converters);
|
||||
writerList = Arrays.asList(writers);
|
||||
}
|
||||
FormattingConversionService service = new DefaultFormattingConversionService();
|
||||
service.addConverter(new MonoToCompletableFutureConverter());
|
||||
service.addConverter(new ReactorToRxJava1Converter());
|
||||
RequestedContentTypeResolver resolver = new RequestedContentTypeResolverBuilder().build();
|
||||
|
||||
return new ResponseBodyResultHandler(converterList, new DefaultConversionService(), resolver);
|
||||
return new ResponseBodyResultHandler(writerList, new DefaultConversionService(), resolver);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -43,9 +43,9 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.codec.json.JacksonJsonEncoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2Encoder;
|
||||
import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.ResourceHttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.EncoderHttpMessageWriter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageWriter;
|
||||
import org.springframework.http.converter.reactive.ResourceHttpMessageWriter;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
@@ -68,7 +68,7 @@ import static org.springframework.core.ResolvableType.forClassWithGenerics;
|
||||
* Unit tests for {@link ResponseEntityResultHandler}. When adding a test also
|
||||
* consider whether the logic under test is in a parent class, then see:
|
||||
* <ul>
|
||||
* <li>{@code MessageConverterResultHandlerTests},
|
||||
* <li>{@code MessageWriterResultHandlerTests},
|
||||
* <li>{@code ContentNegotiatingResultHandlerSupportTests}
|
||||
* </ul>
|
||||
* @author Rossen Stoyanchev
|
||||
@@ -89,18 +89,18 @@ public class ResponseEntityResultHandlerTests {
|
||||
this.exchange = new DefaultServerWebExchange(request, this.response, new MockWebSessionManager());
|
||||
}
|
||||
|
||||
private ResponseEntityResultHandler createHandler(HttpMessageConverter<?>... converters) {
|
||||
List<HttpMessageConverter<?>> converterList;
|
||||
if (ObjectUtils.isEmpty(converters)) {
|
||||
converterList = new ArrayList<>();
|
||||
converterList.add(new CodecHttpMessageConverter<>(new ByteBufferEncoder()));
|
||||
converterList.add(new CodecHttpMessageConverter<>(new StringEncoder()));
|
||||
converterList.add(new ResourceHttpMessageConverter());
|
||||
converterList.add(new CodecHttpMessageConverter<>(new Jaxb2Encoder()));
|
||||
converterList.add(new CodecHttpMessageConverter<>(new JacksonJsonEncoder()));
|
||||
private ResponseEntityResultHandler createHandler(HttpMessageWriter<?>... writers) {
|
||||
List<HttpMessageWriter<?>> writerList;
|
||||
if (ObjectUtils.isEmpty(writers)) {
|
||||
writerList = new ArrayList<>();
|
||||
writerList.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
|
||||
writerList.add(new EncoderHttpMessageWriter<>(new StringEncoder()));
|
||||
writerList.add(new ResourceHttpMessageWriter());
|
||||
writerList.add(new EncoderHttpMessageWriter<>(new Jaxb2Encoder()));
|
||||
writerList.add(new EncoderHttpMessageWriter<>(new JacksonJsonEncoder()));
|
||||
}
|
||||
else {
|
||||
converterList = Arrays.asList(converters);
|
||||
writerList = Arrays.asList(writers);
|
||||
}
|
||||
FormattingConversionService service = new DefaultFormattingConversionService();
|
||||
service.addConverter(new MonoToCompletableFutureConverter());
|
||||
@@ -109,7 +109,7 @@ public class ResponseEntityResultHandlerTests {
|
||||
|
||||
RequestedContentTypeResolver resolver = new RequestedContentTypeResolverBuilder().build();
|
||||
|
||||
return new ResponseEntityResultHandler(converterList, service, resolver);
|
||||
return new ResponseEntityResultHandler(writerList, service, resolver);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -16,13 +16,8 @@
|
||||
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import static org.junit.Assume.assumeFalse;
|
||||
import static org.springframework.web.client.reactive.ClientWebRequestBuilders.*;
|
||||
import static org.springframework.web.client.reactive.ResponseExtractors.*;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -34,18 +29,13 @@ import reactor.test.TestSubscriber;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.codec.ByteBufferDecoder;
|
||||
import org.springframework.core.codec.ByteBufferEncoder;
|
||||
import org.springframework.core.codec.Encoder;
|
||||
import org.springframework.core.codec.StringDecoder;
|
||||
import org.springframework.core.codec.StringEncoder;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
import org.springframework.http.codec.SseEventEncoder;
|
||||
import org.springframework.http.codec.SseEvent;
|
||||
import org.springframework.http.codec.json.JacksonJsonDecoder;
|
||||
import org.springframework.http.codec.json.JacksonJsonEncoder;
|
||||
import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.DecoderHttpMessageReader;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
import org.springframework.http.server.reactive.AbstractHttpHandlerIntegrationTests;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.http.server.reactive.bootstrap.JettyHttpServer;
|
||||
@@ -54,9 +44,12 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.reactive.WebClient;
|
||||
import org.springframework.web.reactive.DispatcherHandler;
|
||||
import org.springframework.web.reactive.config.WebReactiveConfiguration;
|
||||
import org.springframework.http.codec.SseEvent;
|
||||
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||
|
||||
import static org.junit.Assume.assumeFalse;
|
||||
import static org.springframework.web.client.reactive.ClientWebRequestBuilders.get;
|
||||
import static org.springframework.web.client.reactive.ResponseExtractors.bodyStream;
|
||||
|
||||
/**
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
@@ -74,11 +67,6 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests {
|
||||
assumeFalse(server instanceof JettyHttpServer);
|
||||
|
||||
this.webClient = new WebClient(new ReactorClientHttpConnector());
|
||||
List<HttpMessageConverter<?>> converters = new ArrayList<>();
|
||||
converters.add(new CodecHttpMessageConverter<>(new ByteBufferEncoder(), new ByteBufferDecoder()));
|
||||
converters.add(new CodecHttpMessageConverter<>(new StringEncoder(), new StringDecoder(false)));
|
||||
converters.add(new CodecHttpMessageConverter<>(new JacksonJsonEncoder(), new JacksonJsonDecoder()));
|
||||
this.webClient.setMessageConverters(converters);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -110,16 +98,14 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sseAsPojo() throws Exception {
|
||||
public void sseAsPerson() throws Exception {
|
||||
Mono<String> result = this.webClient
|
||||
.perform(get("http://localhost:" + port + "/sse/person")
|
||||
.accept(new MediaType("text", "event-stream")))
|
||||
.extract(bodyStream(String.class))
|
||||
.filter(s -> !s.equals("\n"))
|
||||
.map(s -> (s.replace("\n", "")))
|
||||
.takeUntil(s -> {
|
||||
return s.endsWith("foo 1\"}");
|
||||
})
|
||||
.map(s -> s.replace("\n", ""))
|
||||
.takeUntil(s -> s.endsWith("foo 1\"}"))
|
||||
.reduce((s1, s2) -> s1 + s2);
|
||||
|
||||
TestSubscriber
|
||||
@@ -135,7 +121,7 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests {
|
||||
.accept(new MediaType("text", "event-stream")))
|
||||
.extract(bodyStream(String.class))
|
||||
.filter(s -> !s.equals("\n"))
|
||||
.map(s -> (s.replace("\n", "")))
|
||||
.map(s -> s.replace("\n", ""))
|
||||
.take(2);
|
||||
|
||||
TestSubscriber
|
||||
@@ -182,12 +168,6 @@ public class SseIntegrationTests extends AbstractHttpHandlerIntegrationTests {
|
||||
public SseController sseController() {
|
||||
return new SseController();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
Encoder<Object> sseEncoder = new SseEventEncoder(Arrays.asList(new JacksonJsonEncoder()));
|
||||
converters.add(new CodecHttpMessageConverter<>(sseEncoder));
|
||||
}
|
||||
}
|
||||
|
||||
private static class Person {
|
||||
|
||||
@@ -58,12 +58,12 @@ import static org.junit.Assert.fail;
|
||||
|
||||
|
||||
/**
|
||||
* Unit tests for {@link HttpMessageConverterView}.
|
||||
* Unit tests for {@link HttpMessageWriterView}.
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class HttpMessageConverterViewTests {
|
||||
public class HttpMessageWriterViewTests {
|
||||
|
||||
private HttpMessageConverterView view = new HttpMessageConverterView(new JacksonJsonEncoder());
|
||||
private HttpMessageWriterView view = new HttpMessageWriterView(new JacksonJsonEncoder());
|
||||
|
||||
private HandlerResult result;
|
||||
|
||||
@@ -123,7 +123,7 @@ public class HttpMessageConverterViewTests {
|
||||
|
||||
@Test
|
||||
public void extractObjectMultipleMatchesNotSupported() throws Exception {
|
||||
HttpMessageConverterView view = new HttpMessageConverterView(new StringEncoder());
|
||||
HttpMessageWriterView view = new HttpMessageWriterView(new StringEncoder());
|
||||
view.setModelKeys(new HashSet<>(Arrays.asList("foo1", "foo2")));
|
||||
this.model.addAttribute("foo1", "bar1");
|
||||
this.model.addAttribute("foo2", "bar2");
|
||||
@@ -140,7 +140,7 @@ public class HttpMessageConverterViewTests {
|
||||
|
||||
@Test
|
||||
public void extractObjectNotSupported() throws Exception {
|
||||
HttpMessageConverterView view = new HttpMessageConverterView(new Jaxb2Encoder());
|
||||
HttpMessageWriterView view = new HttpMessageWriterView(new Jaxb2Encoder());
|
||||
view.setModelKeys(new HashSet<>(Collections.singletonList("foo1")));
|
||||
this.model.addAttribute("foo1", "bar1");
|
||||
|
||||
@@ -90,8 +90,7 @@ public class SseEventEncoder extends AbstractEncoder<Object> {
|
||||
|
||||
Object data = event.getData();
|
||||
Flux<DataBuffer> dataBuffer = Flux.empty();
|
||||
MediaType mediaType = (event.getMediaType() == null ?
|
||||
MediaType.ALL : event.getMediaType());
|
||||
MediaType mediaType = (event.getMediaType() == null ? MediaType.ALL : event.getMediaType());
|
||||
if (data != null) {
|
||||
sb.append("data:");
|
||||
if (data instanceof String) {
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.http.converter.reactive;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.Decoder;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ReactiveHttpInputMessage;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link HttpMessageReader} interface that delegates to
|
||||
* a {@link Decoder}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Sebastien Deleuze
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class DecoderHttpMessageReader<T> implements HttpMessageReader<T> {
|
||||
|
||||
private final Decoder<T> decoder;
|
||||
|
||||
private final List<MediaType> readableMediaTypes;
|
||||
|
||||
|
||||
/**
|
||||
* Create a {@code CodecHttpMessageConverter} with the given {@link Decoder}.
|
||||
* @param decoder the decoder to use
|
||||
*/
|
||||
public DecoderHttpMessageReader(Decoder<T> decoder) {
|
||||
this.decoder = decoder;
|
||||
this.readableMediaTypes = decoder != null ?
|
||||
MediaType.toMediaTypes(decoder.getDecodableMimeTypes()) :
|
||||
Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean canRead(ResolvableType type, MediaType mediaType) {
|
||||
return this.decoder != null && this.decoder.canDecode(type, mediaType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MediaType> getReadableMediaTypes() {
|
||||
return this.readableMediaTypes;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Flux<T> read(ResolvableType type, ReactiveHttpInputMessage inputMessage) {
|
||||
if (this.decoder == null) {
|
||||
return Flux.error(new IllegalStateException("No decoder set"));
|
||||
}
|
||||
MediaType contentType = getContentType(inputMessage);
|
||||
return this.decoder.decode(inputMessage.getBody(), type, contentType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<T> readMono(ResolvableType type, ReactiveHttpInputMessage inputMessage) {
|
||||
if (this.decoder == null) {
|
||||
return Mono.error(new IllegalStateException("No decoder set"));
|
||||
}
|
||||
MediaType contentType = getContentType(inputMessage);
|
||||
return this.decoder.decodeToMono(inputMessage.getBody(), type, contentType);
|
||||
}
|
||||
|
||||
private MediaType getContentType(ReactiveHttpInputMessage inputMessage) {
|
||||
MediaType contentType = inputMessage.getHeaders().getContentType();
|
||||
return (contentType != null ? contentType : MediaType.APPLICATION_OCTET_STREAM);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,119 +24,52 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.Decoder;
|
||||
import org.springframework.core.codec.Encoder;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ReactiveHttpInputMessage;
|
||||
import org.springframework.http.ReactiveHttpOutputMessage;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link HttpMessageConverter} interface that delegates to
|
||||
* {@link Encoder} and {@link Decoder}.
|
||||
* Implementation of the {@link HttpMessageWriter} interface that delegates to
|
||||
* an {@link Encoder}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Sebastien Deleuze
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class CodecHttpMessageConverter<T> implements HttpMessageConverter<T> {
|
||||
public class EncoderHttpMessageWriter<T> implements HttpMessageWriter<T> {
|
||||
|
||||
private final Encoder<T> encoder;
|
||||
|
||||
private final Decoder<T> decoder;
|
||||
|
||||
private final List<MediaType> readableMediaTypes;
|
||||
|
||||
private final List<MediaType> writableMediaTypes;
|
||||
|
||||
|
||||
/**
|
||||
* Create a {@code CodecHttpMessageConverter} with the given {@link Encoder}. When
|
||||
* using this constructor, all read-related methods will in {@code false} or an
|
||||
* {@link IllegalStateException}.
|
||||
* Create a {@code CodecHttpMessageConverter} with the given {@link Encoder}.
|
||||
* @param encoder the encoder to use
|
||||
*/
|
||||
public CodecHttpMessageConverter(Encoder<T> encoder) {
|
||||
this(encoder, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code CodecHttpMessageConverter} with the given {@link Decoder}. When
|
||||
* using this constructor, all write-related methods will in {@code false} or an
|
||||
* {@link IllegalStateException}.
|
||||
* @param decoder the decoder to use
|
||||
*/
|
||||
public CodecHttpMessageConverter(Decoder<T> decoder) {
|
||||
this(null, decoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code CodecHttpMessageConverter} with the given {@link Encoder} and
|
||||
* {@link Decoder}.
|
||||
* @param encoder the encoder to use, can be {@code null}
|
||||
* @param decoder the decoder to use, can be {@code null}
|
||||
*/
|
||||
public CodecHttpMessageConverter(Encoder<T> encoder, Decoder<T> decoder) {
|
||||
public EncoderHttpMessageWriter(Encoder<T> encoder) {
|
||||
this.encoder = encoder;
|
||||
this.decoder = decoder;
|
||||
|
||||
this.readableMediaTypes = decoder != null ?
|
||||
MediaType.toMediaTypes(decoder.getDecodableMimeTypes()) :
|
||||
Collections.emptyList();
|
||||
this.writableMediaTypes = encoder != null ?
|
||||
MediaType.toMediaTypes(encoder.getEncodableMimeTypes()) :
|
||||
Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean canRead(ResolvableType type, MediaType mediaType) {
|
||||
return this.decoder != null && this.decoder.canDecode(type, mediaType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canWrite(ResolvableType type, MediaType mediaType) {
|
||||
return this.encoder != null && this.encoder.canEncode(type, mediaType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MediaType> getReadableMediaTypes() {
|
||||
return this.readableMediaTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MediaType> getWritableMediaTypes() {
|
||||
return this.writableMediaTypes;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Flux<T> read(ResolvableType type, ReactiveHttpInputMessage inputMessage) {
|
||||
if (this.decoder == null) {
|
||||
return Flux.error(new IllegalStateException("No decoder set"));
|
||||
}
|
||||
MediaType contentType = getContentType(inputMessage);
|
||||
return this.decoder.decode(inputMessage.getBody(), type, contentType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<T> readMono(ResolvableType type, ReactiveHttpInputMessage inputMessage) {
|
||||
if (this.decoder == null) {
|
||||
return Mono.error(new IllegalStateException("No decoder set"));
|
||||
}
|
||||
MediaType contentType = getContentType(inputMessage);
|
||||
return this.decoder.decodeToMono(inputMessage.getBody(), type, contentType);
|
||||
}
|
||||
|
||||
private MediaType getContentType(ReactiveHttpInputMessage inputMessage) {
|
||||
MediaType contentType = inputMessage.getHeaders().getContentType();
|
||||
return (contentType != null ? contentType : MediaType.APPLICATION_OCTET_STREAM);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<Void> write(Publisher<? extends T> inputStream, ResolvableType type,
|
||||
MediaType contentType, ReactiveHttpOutputMessage outputMessage) {
|
||||
@@ -28,13 +28,14 @@ import org.springframework.http.ReactiveHttpInputMessage;
|
||||
import org.springframework.http.ReactiveHttpOutputMessage;
|
||||
|
||||
/**
|
||||
* Strategy interface that specifies a converter that can convert from and to HTTP
|
||||
* requests and responses.
|
||||
* Strategy interface that specifies a reader that can convert from the HTTP
|
||||
* request body from a stream of bytes to Objects.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.0
|
||||
*/
|
||||
public interface HttpMessageConverter<T> {
|
||||
public interface HttpMessageReader<T> {
|
||||
|
||||
/**
|
||||
* Indicates whether the given class can be read by this converter.
|
||||
@@ -71,31 +72,4 @@ public interface HttpMessageConverter<T> {
|
||||
*/
|
||||
Mono<T> readMono(ResolvableType type, ReactiveHttpInputMessage inputMessage);
|
||||
|
||||
/**
|
||||
* Indicates whether the given class can be written by this converter.
|
||||
* @param type the class to test for writability
|
||||
* @param mediaType the media type to write, can be {@code null} if not specified.
|
||||
* Typically the value of an {@code Accept} header.
|
||||
* @return {@code true} if writable; {@code false} otherwise
|
||||
*/
|
||||
boolean canWrite(ResolvableType type, MediaType mediaType);
|
||||
|
||||
/**
|
||||
* Return the list of {@link MediaType} objects that can be written by this converter.
|
||||
* @return the list of supported readable media types
|
||||
*/
|
||||
List<MediaType> getWritableMediaTypes();
|
||||
|
||||
/**
|
||||
* Write an given object to the given output message.
|
||||
* @param inputStream the input stream to write
|
||||
* @param type the stream element type to process.
|
||||
* @param contentType the content type to use when writing. May be {@code null} to
|
||||
* indicate that the default content type of the converter must be used.
|
||||
* @param outputMessage the message to write to
|
||||
* @return the converted {@link Mono} of object
|
||||
*/
|
||||
Mono<Void> write(Publisher<? extends T> inputStream, ResolvableType type,
|
||||
MediaType contentType, ReactiveHttpOutputMessage outputMessage);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.http.converter.reactive;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ReactiveHttpOutputMessage;
|
||||
|
||||
/**
|
||||
* Strategy interface that specifies a converter that can convert a stream of
|
||||
* Objects to a stream of bytes to be written to the HTTP response body.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.0
|
||||
*/
|
||||
public interface HttpMessageWriter<T> {
|
||||
|
||||
/**
|
||||
* Indicates whether the given class can be written by this converter.
|
||||
* @param type the class to test for writability
|
||||
* @param mediaType the media type to write, can be {@code null} if not specified.
|
||||
* Typically the value of an {@code Accept} header.
|
||||
* @return {@code true} if writable; {@code false} otherwise
|
||||
*/
|
||||
boolean canWrite(ResolvableType type, MediaType mediaType);
|
||||
|
||||
/**
|
||||
* Return the list of {@link MediaType} objects that can be written by this converter.
|
||||
* @return the list of supported readable media types
|
||||
*/
|
||||
List<MediaType> getWritableMediaTypes();
|
||||
|
||||
/**
|
||||
* Write an given object to the given output message.
|
||||
* @param inputStream the input stream to write
|
||||
* @param type the stream element type to process.
|
||||
* @param contentType the content type to use when writing. May be {@code null} to
|
||||
* indicate that the default content type of the converter must be used.
|
||||
* @param outputMessage the message to write to
|
||||
* @return the converted {@link Mono} of object
|
||||
*/
|
||||
Mono<Void> write(Publisher<? extends T> inputStream, ResolvableType type,
|
||||
MediaType contentType, ReactiveHttpOutputMessage outputMessage);
|
||||
|
||||
}
|
||||
@@ -37,27 +37,31 @@ import org.springframework.util.MimeTypeUtils;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
|
||||
/**
|
||||
* Implementation of {@link HttpMessageConverter} that can read and write
|
||||
* {@link Resource Resources} and supports byte range requests.
|
||||
* Implementation of {@link HttpMessageWriter} that can write
|
||||
* {@link Resource Resources}.
|
||||
*
|
||||
* <p>For a Resource reader simply use {@link ResourceDecoder} wrapped with
|
||||
* {@link DecoderHttpMessageReader}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ResourceHttpMessageConverter extends CodecHttpMessageConverter<Resource> {
|
||||
public class ResourceHttpMessageWriter extends EncoderHttpMessageWriter<Resource> {
|
||||
|
||||
public ResourceHttpMessageConverter() {
|
||||
super(new ResourceEncoder(), new ResourceDecoder());
|
||||
|
||||
public ResourceHttpMessageWriter() {
|
||||
super(new ResourceEncoder());
|
||||
}
|
||||
|
||||
public ResourceHttpMessageConverter(int bufferSize) {
|
||||
super(new ResourceEncoder(bufferSize), new ResourceDecoder());
|
||||
public ResourceHttpMessageWriter(int bufferSize) {
|
||||
super(new ResourceEncoder(bufferSize));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<Void> write(Publisher<? extends Resource> inputStream,
|
||||
ResolvableType type, MediaType contentType,
|
||||
ReactiveHttpOutputMessage outputMessage) {
|
||||
public Mono<Void> write(Publisher<? extends Resource> inputStream, ResolvableType type,
|
||||
MediaType contentType, ReactiveHttpOutputMessage outputMessage) {
|
||||
|
||||
return Mono.from(Flux.from(inputStream).
|
||||
take(1).
|
||||
concatMap(resource -> {
|
||||
@@ -68,11 +72,9 @@ public class ResourceHttpMessageConverter extends CodecHttpMessageConverter<Reso
|
||||
}));
|
||||
}
|
||||
|
||||
protected void addHeaders(HttpHeaders headers, Resource resource,
|
||||
MediaType contentType) {
|
||||
protected void addHeaders(HttpHeaders headers, Resource resource, MediaType contentType) {
|
||||
if (headers.getContentType() == null) {
|
||||
if (contentType == null ||
|
||||
!contentType.isConcrete() ||
|
||||
if (contentType == null || !contentType.isConcrete() ||
|
||||
MediaType.APPLICATION_OCTET_STREAM.equals(contentType)) {
|
||||
contentType = MimeTypeUtils.getMimeType(resource.getFilename()).
|
||||
map(MediaType::toMediaType).
|
||||
@@ -87,6 +89,7 @@ public class ResourceHttpMessageConverter extends CodecHttpMessageConverter<Reso
|
||||
|
||||
private Mono<Void> writeContent(Resource resource, ResolvableType type,
|
||||
MediaType contentType, ReactiveHttpOutputMessage outputMessage) {
|
||||
|
||||
if (outputMessage instanceof ZeroCopyHttpOutputMessage) {
|
||||
Optional<File> file = getFile(resource);
|
||||
if (file.isPresent()) {
|
||||
@@ -19,7 +19,7 @@ package org.springframework.web.client.reactive;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.client.reactive.ClientHttpResponse;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
|
||||
/**
|
||||
* Contract to extract the content of a raw {@link ClientHttpResponse} decoding
|
||||
@@ -36,9 +36,9 @@ public interface BodyExtractor<T> {
|
||||
/**
|
||||
* Extract content from the response body
|
||||
* @param clientResponse the raw HTTP response
|
||||
* @param messageConverters the message converters that decode the response body
|
||||
* @param messageReaders the message readers that decode the response body
|
||||
* @return the relevant content
|
||||
*/
|
||||
T extract(ClientHttpResponse clientResponse, List<HttpMessageConverter<?>> messageConverters);
|
||||
T extract(ClientHttpResponse clientResponse, List<HttpMessageReader<?>> messageReaders);
|
||||
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import java.util.List;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.reactive.ClientHttpResponse;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link ResponseErrorHandler} interface
|
||||
@@ -33,13 +33,13 @@ import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
public class DefaultResponseErrorHandler implements ResponseErrorHandler {
|
||||
|
||||
@Override
|
||||
public void handleError(ClientHttpResponse response, List<HttpMessageConverter<?>> messageConverters) {
|
||||
public void handleError(ClientHttpResponse response, List<HttpMessageReader<?>> messageReaders) {
|
||||
HttpStatus responseStatus = response.getStatusCode();
|
||||
if (responseStatus.is4xxClientError()) {
|
||||
throw new WebClientErrorException(response, messageConverters);
|
||||
throw new WebClientErrorException(response, messageReaders);
|
||||
}
|
||||
if (responseStatus.is5xxServerError()) {
|
||||
throw new WebServerErrorException(response, messageConverters);
|
||||
throw new WebServerErrorException(response, messageReaders);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ package org.springframework.web.client.reactive;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.client.reactive.ClientHttpResponse;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
|
||||
/**
|
||||
* Strategy interface used by the {@link WebClient} to handle errors in
|
||||
@@ -37,6 +37,6 @@ public interface ResponseErrorHandler {
|
||||
* {@link ClientHttpResponse#getStatusCode() HttpStatus} of the response and
|
||||
* throw {@link WebClientException}s in case of errors.
|
||||
*/
|
||||
void handleError(ClientHttpResponse response, List<HttpMessageConverter<?>> messageConverters);
|
||||
void handleError(ClientHttpResponse response, List<HttpMessageReader<?>> messageReaders);
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.reactive.ClientHttpResponse;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
|
||||
/**
|
||||
* Static factory methods for {@link ResponseExtractor} and {@link BodyExtractor},
|
||||
@@ -47,9 +47,9 @@ public class ResponseExtractors {
|
||||
public static <T> ResponseExtractor<Mono<T>> body(ResolvableType bodyType) {
|
||||
return (clientResponse, webClientConfig) -> (Mono<T>) clientResponse
|
||||
.doOnNext(response -> webClientConfig.getResponseErrorHandler()
|
||||
.handleError(response, webClientConfig.getMessageConverters()))
|
||||
.handleError(response, webClientConfig.getMessageReaders()))
|
||||
.flatMap(resp -> decodeResponseBodyAsMono(resp, bodyType,
|
||||
webClientConfig.getMessageConverters()))
|
||||
webClientConfig.getMessageReaders()))
|
||||
.next();
|
||||
}
|
||||
|
||||
@@ -86,8 +86,8 @@ public class ResponseExtractors {
|
||||
public static <T> ResponseExtractor<Flux<T>> bodyStream(ResolvableType bodyType) {
|
||||
return (clientResponse, webClientConfig) -> clientResponse
|
||||
.doOnNext(response -> webClientConfig.getResponseErrorHandler()
|
||||
.handleError(response, webClientConfig.getMessageConverters()))
|
||||
.flatMap(resp -> decodeResponseBody(resp, bodyType, webClientConfig.getMessageConverters()));
|
||||
.handleError(response, webClientConfig.getMessageReaders()))
|
||||
.flatMap(resp -> decodeResponseBody(resp, bodyType, webClientConfig.getMessageReaders()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,7 +127,7 @@ public class ResponseExtractors {
|
||||
return (clientResponse, webClientConfig) -> clientResponse.then(response ->
|
||||
Mono.when(
|
||||
decodeResponseBodyAsMono(response, bodyType,
|
||||
webClientConfig.getMessageConverters()).defaultIfEmpty(EMPTY_BODY),
|
||||
webClientConfig.getMessageReaders()).defaultIfEmpty(EMPTY_BODY),
|
||||
Mono.just(response.getHeaders()),
|
||||
Mono.just(response.getStatusCode()))
|
||||
).map(tuple -> {
|
||||
@@ -153,7 +153,7 @@ public class ResponseExtractors {
|
||||
public static <T> ResponseExtractor<Mono<ResponseEntity<Flux<T>>>> responseStream(ResolvableType type) {
|
||||
return (clientResponse, webClientConfig) -> clientResponse
|
||||
.map(response -> new ResponseEntity<>(
|
||||
decodeResponseBody(response, type, webClientConfig.getMessageConverters()),
|
||||
decodeResponseBody(response, type, webClientConfig.getMessageReaders()),
|
||||
response.getHeaders(), response.getStatusCode()));
|
||||
}
|
||||
|
||||
@@ -175,26 +175,26 @@ public class ResponseExtractors {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected static <T> Flux<T> decodeResponseBody(ClientHttpResponse response,
|
||||
ResolvableType responseType, List<HttpMessageConverter<?>> messageConverters) {
|
||||
ResolvableType responseType, List<HttpMessageReader<?>> messageReaders) {
|
||||
|
||||
MediaType contentType = response.getHeaders().getContentType();
|
||||
HttpMessageConverter<?> converter = resolveConverter(messageConverters, responseType, contentType);
|
||||
return (Flux<T>) converter.read(responseType, response);
|
||||
HttpMessageReader<?> reader = resolveMessageReader(messageReaders, responseType, contentType);
|
||||
return (Flux<T>) reader.read(responseType, response);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected static <T> Mono<T> decodeResponseBodyAsMono(ClientHttpResponse response,
|
||||
ResolvableType responseType, List<HttpMessageConverter<?>> messageConverters) {
|
||||
ResolvableType responseType, List<HttpMessageReader<?>> messageReaders) {
|
||||
|
||||
MediaType contentType = response.getHeaders().getContentType();
|
||||
HttpMessageConverter<?> converter = resolveConverter(messageConverters, responseType, contentType);
|
||||
return (Mono<T>) converter.readMono(responseType, response);
|
||||
HttpMessageReader<?> reader = resolveMessageReader(messageReaders, responseType, contentType);
|
||||
return (Mono<T>) reader.readMono(responseType, response);
|
||||
}
|
||||
|
||||
protected static HttpMessageConverter<?> resolveConverter(
|
||||
List<HttpMessageConverter<?>> messageConverters, ResolvableType responseType, MediaType contentType) {
|
||||
protected static HttpMessageReader<?> resolveMessageReader(List<HttpMessageReader<?>> messageReaders,
|
||||
ResolvableType responseType, MediaType contentType) {
|
||||
|
||||
return messageConverters.stream()
|
||||
return messageReaders.stream()
|
||||
.filter(e -> e.canRead(responseType, contentType))
|
||||
.findFirst()
|
||||
.orElseThrow(() ->
|
||||
|
||||
@@ -25,30 +25,30 @@ import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.Decoder;
|
||||
import org.springframework.core.codec.Encoder;
|
||||
import org.springframework.core.codec.ByteBufferDecoder;
|
||||
import org.springframework.core.codec.ByteBufferEncoder;
|
||||
import org.springframework.http.codec.json.JacksonJsonDecoder;
|
||||
import org.springframework.http.codec.json.JacksonJsonEncoder;
|
||||
import org.springframework.core.codec.ResourceDecoder;
|
||||
import org.springframework.core.codec.StringDecoder;
|
||||
import org.springframework.core.codec.StringEncoder;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.http.client.reactive.ClientHttpRequest;
|
||||
import org.springframework.http.client.reactive.ClientHttpResponse;
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.http.codec.json.JacksonJsonDecoder;
|
||||
import org.springframework.http.codec.json.JacksonJsonEncoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2Decoder;
|
||||
import org.springframework.http.codec.xml.Jaxb2Encoder;
|
||||
import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.ResourceHttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.DecoderHttpMessageReader;
|
||||
import org.springframework.http.converter.reactive.EncoderHttpMessageWriter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
import org.springframework.http.converter.reactive.HttpMessageWriter;
|
||||
import org.springframework.http.converter.reactive.ResourceHttpMessageWriter;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* Reactive Web client supporting the HTTP/1.1 protocol
|
||||
*
|
||||
@@ -96,6 +96,7 @@ public final class WebClient {
|
||||
|
||||
private final DefaultWebClientConfig webClientConfig;
|
||||
|
||||
|
||||
/**
|
||||
* Create a {@code WebClient} instance, using the {@link ClientHttpConnector}
|
||||
* implementation given as an argument to drive the underlying
|
||||
@@ -114,39 +115,54 @@ public final class WebClient {
|
||||
public WebClient(ClientHttpConnector clientHttpConnector) {
|
||||
this.clientHttpConnector = clientHttpConnector;
|
||||
this.webClientConfig = new DefaultWebClientConfig();
|
||||
List<HttpMessageConverter<?>> converters = new ArrayList<>();
|
||||
addDefaultHttpMessageConverters(converters);
|
||||
this.webClientConfig.setMessageConverters(converters);
|
||||
this.webClientConfig.setResponseErrorHandler(new DefaultResponseErrorHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds default HTTP message converters
|
||||
* Adds default HTTP message readers.
|
||||
*/
|
||||
protected final void addDefaultHttpMessageConverters(
|
||||
List<HttpMessageConverter<?>> converters) {
|
||||
converters.add(converter(new ByteBufferEncoder(), new ByteBufferDecoder()));
|
||||
converters.add(converter(new StringEncoder(), new StringDecoder()));
|
||||
converters.add(new ResourceHttpMessageConverter());
|
||||
protected final void addDefaultHttpMessageReaders(List<HttpMessageReader<?>> messageReaders) {
|
||||
messageReaders.add(new DecoderHttpMessageReader<>(new ByteBufferDecoder()));
|
||||
messageReaders.add(new DecoderHttpMessageReader<>(new StringDecoder(false)));
|
||||
messageReaders.add(new DecoderHttpMessageReader<>(new ResourceDecoder()));
|
||||
if (jaxb2Present) {
|
||||
converters.add(converter(new Jaxb2Encoder(), new Jaxb2Decoder()));
|
||||
messageReaders.add(new DecoderHttpMessageReader<>(new Jaxb2Decoder()));
|
||||
}
|
||||
if (jackson2Present) {
|
||||
converters.add(converter(new JacksonJsonEncoder(), new JacksonJsonDecoder()));
|
||||
messageReaders.add(new DecoderHttpMessageReader<>(new JacksonJsonDecoder()));
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> HttpMessageConverter<T> converter(Encoder<T> encoder,
|
||||
Decoder<T> decoder) {
|
||||
return new CodecHttpMessageConverter<>(encoder, decoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of {@link HttpMessageConverter}s to use for encoding and decoding HTTP
|
||||
* messages
|
||||
* Adds default HTTP message writers.
|
||||
*/
|
||||
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
|
||||
this.webClientConfig.setMessageConverters(messageConverters);
|
||||
protected final void addDefaultHttpMessageWriters(List<HttpMessageWriter<?>> messageWriters) {
|
||||
messageWriters.add(new EncoderHttpMessageWriter<>(new ByteBufferEncoder()));
|
||||
messageWriters.add(new EncoderHttpMessageWriter<>(new StringEncoder()));
|
||||
messageWriters.add(new ResourceHttpMessageWriter());
|
||||
if (jaxb2Present) {
|
||||
messageWriters.add(new EncoderHttpMessageWriter<>(new Jaxb2Encoder()));
|
||||
}
|
||||
if (jackson2Present) {
|
||||
messageWriters.add(new EncoderHttpMessageWriter<>(new JacksonJsonEncoder()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the list of {@link HttpMessageReader}s to use for decoding the HTTP
|
||||
* response body.
|
||||
*/
|
||||
public void setMessageReaders(List<HttpMessageReader<?>> messageReaders) {
|
||||
this.webClientConfig.setMessageReaders(messageReaders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of {@link HttpMessageWriter}s to use for encoding the HTTP
|
||||
* request body.
|
||||
*/
|
||||
public void setMessageWriters(List<HttpMessageWriter<?>> messageWrters) {
|
||||
this.webClientConfig.setMessageWriters(messageWrters);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,7 +179,7 @@ public final class WebClient {
|
||||
* Requesting from the exposed {@code Flux} will result in:
|
||||
* <ul>
|
||||
* <li>building the actual HTTP request using the provided {@code ClientWebRequestBuilder}</li>
|
||||
* <li>encoding the HTTP request body with the configured {@code HttpMessageConverter}s</li>
|
||||
* <li>encoding the HTTP request body with the configured {@code HttpMessageWriter}s</li>
|
||||
* <li>returning the response with a publisher of the body</li>
|
||||
* </ul>
|
||||
*/
|
||||
@@ -191,17 +207,36 @@ public final class WebClient {
|
||||
|
||||
protected class DefaultWebClientConfig implements WebClientConfig {
|
||||
|
||||
private List<HttpMessageConverter<?>> messageConverters;
|
||||
private List<HttpMessageReader<?>> messageReaders;
|
||||
|
||||
private List<HttpMessageWriter<?>> messageWriters;
|
||||
|
||||
private ResponseErrorHandler responseErrorHandler;
|
||||
|
||||
@Override
|
||||
public List<HttpMessageConverter<?>> getMessageConverters() {
|
||||
return messageConverters;
|
||||
|
||||
public DefaultWebClientConfig() {
|
||||
this.messageReaders = new ArrayList<>();
|
||||
addDefaultHttpMessageReaders(this.messageReaders);
|
||||
this.messageWriters = new ArrayList<>();
|
||||
addDefaultHttpMessageWriters(this.messageWriters);
|
||||
}
|
||||
|
||||
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
|
||||
this.messageConverters = messageConverters;
|
||||
@Override
|
||||
public List<HttpMessageReader<?>> getMessageReaders() {
|
||||
return this.messageReaders;
|
||||
}
|
||||
|
||||
public void setMessageReaders(List<HttpMessageReader<?>> messageReaders) {
|
||||
this.messageReaders = messageReaders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HttpMessageWriter<?>> getMessageWriters() {
|
||||
return this.messageWriters;
|
||||
}
|
||||
|
||||
public void setMessageWriters(List<HttpMessageWriter<?>> messageWriters) {
|
||||
this.messageWriters = messageWriters;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -218,10 +253,12 @@ public final class WebClient {
|
||||
|
||||
private final ClientWebRequest clientWebRequest;
|
||||
|
||||
|
||||
public DefaultRequestCallback(ClientWebRequest clientWebRequest) {
|
||||
this.clientWebRequest = clientWebRequest;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<Void> apply(ClientHttpRequest clientHttpRequest) {
|
||||
clientHttpRequest.getHeaders().putAll(this.clientWebRequest.getHttpHeaders());
|
||||
@@ -229,13 +266,13 @@ public final class WebClient {
|
||||
clientHttpRequest.getHeaders().setAccept(
|
||||
Collections.singletonList(MediaType.ALL));
|
||||
}
|
||||
clientWebRequest.getCookies().values()
|
||||
this.clientWebRequest.getCookies().values()
|
||||
.stream().flatMap(cookies -> cookies.stream())
|
||||
.forEach(cookie -> clientHttpRequest.getCookies().add(cookie.getName(), cookie));
|
||||
if (this.clientWebRequest.getBody() != null) {
|
||||
return writeRequestBody(this.clientWebRequest.getBody(),
|
||||
this.clientWebRequest.getElementType(),
|
||||
clientHttpRequest, webClientConfig.getMessageConverters());
|
||||
clientHttpRequest, WebClient.this.webClientConfig.getMessageWriters());
|
||||
}
|
||||
else {
|
||||
return clientHttpRequest.setComplete();
|
||||
@@ -245,22 +282,22 @@ public final class WebClient {
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
protected Mono<Void> writeRequestBody(Publisher<?> content,
|
||||
ResolvableType requestType, ClientHttpRequest request,
|
||||
List<HttpMessageConverter<?>> messageConverters) {
|
||||
List<HttpMessageWriter<?>> messageWriters) {
|
||||
|
||||
MediaType contentType = request.getHeaders().getContentType();
|
||||
Optional<HttpMessageConverter<?>> converter = resolveConverter(messageConverters, requestType, contentType);
|
||||
if (!converter.isPresent()) {
|
||||
Optional<HttpMessageWriter<?>> messageWriter = resolveWriter(messageWriters, requestType, contentType);
|
||||
if (!messageWriter.isPresent()) {
|
||||
return Mono.error(new IllegalStateException(
|
||||
"Could not encode request body of type '" + contentType
|
||||
+ "' with target type '" + requestType.toString() + "'"));
|
||||
}
|
||||
return converter.get().write((Publisher) content, requestType, contentType, request);
|
||||
return messageWriter.get().write((Publisher) content, requestType, contentType, request);
|
||||
}
|
||||
|
||||
protected Optional<HttpMessageConverter<?>> resolveConverter(
|
||||
List<HttpMessageConverter<?>> messageConverters, ResolvableType type,
|
||||
MediaType mediaType) {
|
||||
return messageConverters.stream().filter(e -> e.canWrite(type, mediaType)).findFirst();
|
||||
protected Optional<HttpMessageWriter<?>> resolveWriter(List<HttpMessageWriter<?>> messageWriters,
|
||||
ResolvableType type, MediaType mediaType) {
|
||||
|
||||
return messageWriters.stream().filter(e -> e.canWrite(type, mediaType)).findFirst();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@ package org.springframework.web.client.reactive;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
import org.springframework.http.converter.reactive.HttpMessageWriter;
|
||||
|
||||
/**
|
||||
* Interface that makes the {@link WebClient} configuration information
|
||||
@@ -30,9 +31,14 @@ import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
public interface WebClientConfig {
|
||||
|
||||
/**
|
||||
* Return the message converters that can help encoding/decoding the HTTP message body
|
||||
* Return the message readers that can help decoding the HTTP response body
|
||||
*/
|
||||
List<HttpMessageConverter<?>> getMessageConverters();
|
||||
List<HttpMessageReader<?>> getMessageReaders();
|
||||
|
||||
/**
|
||||
* Return the message writers that can help encode the HTTP request body
|
||||
*/
|
||||
List<HttpMessageWriter<?>> getMessageWriters();
|
||||
|
||||
/**
|
||||
* Return the configured {@link ResponseErrorHandler}
|
||||
|
||||
@@ -19,7 +19,7 @@ package org.springframework.web.client.reactive;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.client.reactive.ClientHttpResponse;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
|
||||
/**
|
||||
* Exception thrown when an HTTP 4xx is received.
|
||||
@@ -33,14 +33,14 @@ public class WebClientErrorException extends WebClientResponseException {
|
||||
|
||||
/**
|
||||
* Construct a new instance of {@code HttpClientErrorException} based on a
|
||||
* {@link ClientHttpResponse} and {@link HttpMessageConverter}s to optionally
|
||||
* {@link ClientHttpResponse} and {@link HttpMessageReader}s to optionally
|
||||
* help decoding the response body
|
||||
*
|
||||
* @param response the HTTP response
|
||||
* @param converters the message converters that may decode the HTTP response body
|
||||
* @param messageReaders the message converters that may decode the HTTP response body
|
||||
*/
|
||||
public WebClientErrorException(ClientHttpResponse response, List<HttpMessageConverter<?>> converters) {
|
||||
super(initMessage(response), response, converters);
|
||||
public WebClientErrorException(ClientHttpResponse response, List<HttpMessageReader<?>> messageReaders) {
|
||||
super(initMessage(response), response, messageReaders);
|
||||
}
|
||||
|
||||
private static String initMessage(ClientHttpResponse response) {
|
||||
|
||||
@@ -21,7 +21,7 @@ import java.util.List;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.reactive.ClientHttpResponse;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
|
||||
/**
|
||||
* Base class for exceptions associated with specific HTTP client response
|
||||
@@ -35,20 +35,20 @@ public class WebClientResponseException extends WebClientException {
|
||||
|
||||
private final ClientHttpResponse clientResponse;
|
||||
|
||||
private final List<HttpMessageConverter<?>> messageConverters;
|
||||
private final List<HttpMessageReader<?>> messageReaders;
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new instance of {@code WebClientResponseException} with the given response data
|
||||
* @param message the given error message
|
||||
* @param clientResponse the HTTP response
|
||||
* @param messageConverters the message converters that maay decode the HTTP response body
|
||||
* @param messageReaders the message converters that maay decode the HTTP response body
|
||||
*/
|
||||
public WebClientResponseException(String message, ClientHttpResponse clientResponse,
|
||||
List<HttpMessageConverter<?>> messageConverters) {
|
||||
List<HttpMessageReader<?>> messageReaders) {
|
||||
super(message);
|
||||
this.clientResponse = clientResponse;
|
||||
this.messageConverters = messageConverters;
|
||||
this.messageReaders = messageReaders;
|
||||
}
|
||||
|
||||
|
||||
@@ -76,6 +76,6 @@ public class WebClientResponseException extends WebClientException {
|
||||
* </pre>
|
||||
*/
|
||||
public <T> T getResponseBody(BodyExtractor<T> extractor) {
|
||||
return extractor.extract(this.clientResponse, this.messageConverters);
|
||||
return extractor.extract(this.clientResponse, this.messageReaders);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,8 @@ package org.springframework.web.client.reactive;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.reactive.ClientHttpResponse;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
|
||||
/**
|
||||
* Exception thrown when an HTTP 5xx is received.
|
||||
@@ -33,13 +32,13 @@ public class WebServerErrorException extends WebClientResponseException {
|
||||
|
||||
/**
|
||||
* Construct a new instance of {@code HttpServerErrorException} based on a
|
||||
* {@link ClientHttpResponse} and {@link HttpMessageConverter}s to optionally
|
||||
* {@link ClientHttpResponse} and {@link HttpMessageReader}s to optionally
|
||||
* help decoding the response body
|
||||
* @param response the HTTP response
|
||||
* @param converters the message converters that may decode the HTTP response body
|
||||
* @param messageReaders the message converters that may decode the HTTP response body
|
||||
*/
|
||||
public WebServerErrorException(ClientHttpResponse response, List<HttpMessageConverter<?>> converters) {
|
||||
super(initMessage(response), response, converters);
|
||||
public WebServerErrorException(ClientHttpResponse response, List<HttpMessageReader<?>> messageReaders) {
|
||||
super(initMessage(response), response, messageReaders);
|
||||
}
|
||||
|
||||
private static String initMessage(ClientHttpResponse response) {
|
||||
|
||||
@@ -29,7 +29,7 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.reactive.ClientHttpResponse;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
import org.springframework.web.client.reactive.BodyExtractor;
|
||||
import org.springframework.web.client.reactive.ResponseExtractor;
|
||||
import org.springframework.web.client.reactive.WebClientException;
|
||||
@@ -53,8 +53,8 @@ public class RxJava1ResponseExtractors {
|
||||
return (clientResponse, webClientConfig) -> (Single<T>) RxJava1Adapter
|
||||
.publisherToSingle(clientResponse
|
||||
.doOnNext(response -> webClientConfig.getResponseErrorHandler()
|
||||
.handleError(response, webClientConfig.getMessageConverters()))
|
||||
.flatMap(resp -> decodeResponseBodyAsMono(resp, bodyType, webClientConfig.getMessageConverters())));
|
||||
.handleError(response, webClientConfig.getMessageReaders()))
|
||||
.flatMap(resp -> decodeResponseBodyAsMono(resp, bodyType, webClientConfig.getMessageReaders())));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,8 +95,8 @@ public class RxJava1ResponseExtractors {
|
||||
return (clientResponse, webClientConfig) -> RxJava1Adapter
|
||||
.publisherToObservable(clientResponse
|
||||
.doOnNext(response -> webClientConfig.getResponseErrorHandler()
|
||||
.handleError(response, webClientConfig.getMessageConverters()))
|
||||
.flatMap(resp -> decodeResponseBody(resp, bodyType, webClientConfig.getMessageConverters())));
|
||||
.handleError(response, webClientConfig.getMessageReaders()))
|
||||
.flatMap(resp -> decodeResponseBody(resp, bodyType, webClientConfig.getMessageReaders())));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,7 +139,7 @@ public class RxJava1ResponseExtractors {
|
||||
RxJava1Adapter.publisherToSingle(clientResponse
|
||||
.then(response ->
|
||||
Mono.when(
|
||||
decodeResponseBody(response, bodyType, webClientConfig.getMessageConverters()).next(),
|
||||
decodeResponseBody(response, bodyType, webClientConfig.getMessageReaders()).next(),
|
||||
Mono.just(response.getHeaders()),
|
||||
Mono.just(response.getStatusCode())))
|
||||
.map(tuple ->
|
||||
@@ -174,7 +174,7 @@ public class RxJava1ResponseExtractors {
|
||||
return (clientResponse, webClientConfig) -> RxJava1Adapter.publisherToSingle(clientResponse
|
||||
.map(response -> new ResponseEntity<>(
|
||||
RxJava1Adapter
|
||||
.publisherToObservable(decodeResponseBody(response, bodyType, webClientConfig.getMessageConverters())),
|
||||
.publisherToObservable(decodeResponseBody(response, bodyType, webClientConfig.getMessageReaders())),
|
||||
response.getHeaders(),
|
||||
response.getStatusCode())));
|
||||
}
|
||||
@@ -189,26 +189,26 @@ public class RxJava1ResponseExtractors {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected static <T> Flux<T> decodeResponseBody(ClientHttpResponse response,
|
||||
ResolvableType responseType, List<HttpMessageConverter<?>> messageConverters) {
|
||||
ResolvableType responseType, List<HttpMessageReader<?>> messageReaders) {
|
||||
|
||||
MediaType contentType = response.getHeaders().getContentType();
|
||||
HttpMessageConverter<?> converter = resolveConverter(messageConverters, responseType, contentType);
|
||||
HttpMessageReader<?> converter = resolveMessageReader(messageReaders, responseType, contentType);
|
||||
return (Flux<T>) converter.read(responseType, response);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected static <T> Mono<T> decodeResponseBodyAsMono(ClientHttpResponse response,
|
||||
ResolvableType responseType, List<HttpMessageConverter<?>> messageConverters) {
|
||||
ResolvableType responseType, List<HttpMessageReader<?>> messageReaders) {
|
||||
|
||||
MediaType contentType = response.getHeaders().getContentType();
|
||||
HttpMessageConverter<?> converter = resolveConverter(messageConverters, responseType, contentType);
|
||||
HttpMessageReader<?> converter = resolveMessageReader(messageReaders, responseType, contentType);
|
||||
return (Mono<T>) converter.readMono(responseType, response);
|
||||
}
|
||||
|
||||
protected static HttpMessageConverter<?> resolveConverter(
|
||||
List<HttpMessageConverter<?>> messageConverters, ResolvableType responseType, MediaType contentType) {
|
||||
protected static HttpMessageReader<?> resolveMessageReader(List<HttpMessageReader<?>> messageReaders,
|
||||
ResolvableType responseType, MediaType contentType) {
|
||||
|
||||
return messageConverters.stream()
|
||||
return messageReaders.stream()
|
||||
.filter(e -> e.canRead(responseType, contentType))
|
||||
.findFirst()
|
||||
.orElseThrow(() ->
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
package org.springframework.web.client.reactive;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.BDDMockito.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.springframework.web.client.reactive.ResponseExtractors.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -15,15 +9,21 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.test.TestSubscriber;
|
||||
|
||||
import org.springframework.core.codec.StringDecoder;
|
||||
import org.springframework.core.codec.StringEncoder;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.reactive.ClientHttpResponse;
|
||||
import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.DecoderHttpMessageReader;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.springframework.web.client.reactive.ResponseExtractors.as;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DefaultResponseErrorHandler}.
|
||||
@@ -36,20 +36,20 @@ public class DefaultResponseErrorHandlerTests {
|
||||
|
||||
private ClientHttpResponse response;
|
||||
|
||||
private List<HttpMessageConverter<?>> messageConverters;
|
||||
private List<HttpMessageReader<?>> messageReaders;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
this.errorHandler = new DefaultResponseErrorHandler();
|
||||
this.response = mock(ClientHttpResponse.class);
|
||||
this.messageConverters = Collections
|
||||
.singletonList(new CodecHttpMessageConverter<>(new StringEncoder(), new StringDecoder()));
|
||||
this.messageReaders = Collections
|
||||
.singletonList(new DecoderHttpMessageReader<>(new StringDecoder()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noError() throws Exception {
|
||||
given(this.response.getStatusCode()).willReturn(HttpStatus.OK);
|
||||
this.errorHandler.handleError(this.response, this.messageConverters);
|
||||
this.errorHandler.handleError(this.response, this.messageReaders);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -62,7 +62,7 @@ public class DefaultResponseErrorHandlerTests {
|
||||
given(this.response.getHeaders()).willReturn(headers);
|
||||
given(this.response.getBody()).willReturn(Flux.just(buffer));
|
||||
try {
|
||||
this.errorHandler.handleError(this.response, this.messageConverters);
|
||||
this.errorHandler.handleError(this.response, this.messageReaders);
|
||||
fail("expected HttpClientErrorException");
|
||||
}
|
||||
catch (WebClientErrorException exc) {
|
||||
@@ -84,7 +84,7 @@ public class DefaultResponseErrorHandlerTests {
|
||||
given(this.response.getHeaders()).willReturn(headers);
|
||||
given(this.response.getBody()).willReturn(Flux.just(buffer));
|
||||
try {
|
||||
this.errorHandler.handleError(this.response, this.messageConverters);
|
||||
this.errorHandler.handleError(this.response, this.messageReaders);
|
||||
fail("expected HttpServerErrorException");
|
||||
}
|
||||
catch (WebServerErrorException exc) {
|
||||
|
||||
@@ -1,24 +1,17 @@
|
||||
package org.springframework.web.client.reactive;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.BDDMockito.eq;
|
||||
import static org.mockito.BDDMockito.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import reactor.core.Exceptions;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.TestSubscriber;
|
||||
import reactor.core.Exceptions;
|
||||
|
||||
import org.springframework.core.codec.StringDecoder;
|
||||
import org.springframework.core.codec.StringEncoder;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -27,9 +20,18 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.reactive.ClientHttpResponse;
|
||||
import org.springframework.http.codec.json.JacksonJsonDecoder;
|
||||
import org.springframework.http.codec.json.JacksonJsonEncoder;
|
||||
import org.springframework.http.converter.reactive.CodecHttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.HttpMessageConverter;
|
||||
import org.springframework.http.converter.reactive.DecoderHttpMessageReader;
|
||||
import org.springframework.http.converter.reactive.HttpMessageReader;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.BDDMockito.eq;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ResponseExtractors}.
|
||||
@@ -42,7 +44,7 @@ public class ResponseExtractorsTests {
|
||||
|
||||
private ClientHttpResponse response;
|
||||
|
||||
private List<HttpMessageConverter<?>> messageConverters;
|
||||
private List<HttpMessageReader<?>> messageReaders;
|
||||
|
||||
private WebClientConfig webClientConfig;
|
||||
|
||||
@@ -53,12 +55,12 @@ public class ResponseExtractorsTests {
|
||||
this.headers = new HttpHeaders();
|
||||
this.response = mock(ClientHttpResponse.class);
|
||||
given(this.response.getHeaders()).willReturn(headers);
|
||||
this.messageConverters = Arrays.asList(
|
||||
new CodecHttpMessageConverter<>(new StringEncoder(), new StringDecoder()),
|
||||
new CodecHttpMessageConverter<>(new JacksonJsonEncoder(), new JacksonJsonDecoder()));
|
||||
this.messageReaders = Arrays.asList(
|
||||
new DecoderHttpMessageReader<>(new StringDecoder()),
|
||||
new DecoderHttpMessageReader<>(new JacksonJsonDecoder()));
|
||||
this.webClientConfig = mock(WebClientConfig.class);
|
||||
this.errorHandler = mock(ResponseErrorHandler.class);
|
||||
given(this.webClientConfig.getMessageConverters()).willReturn(this.messageConverters);
|
||||
given(this.webClientConfig.getMessageReaders()).willReturn(this.messageReaders);
|
||||
given(this.webClientConfig.getResponseErrorHandler()).willReturn(this.errorHandler);
|
||||
}
|
||||
|
||||
@@ -182,7 +184,7 @@ public class ResponseExtractorsTests {
|
||||
.assertValueCount(1)
|
||||
.assertComplete();
|
||||
|
||||
then(this.errorHandler).should().handleError(eq(this.response), eq(this.messageConverters));
|
||||
then(this.errorHandler).should().handleError(eq(this.response), eq(this.messageReaders));
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user