diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/ViewResolverRegistry.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/ViewResolverRegistry.java index 7fc95f3435..9c083e2d6d 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/ViewResolverRegistry.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/ViewResolverRegistry.java @@ -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. - *

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}. + *

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)); diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/WebReactiveConfiguration.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/WebReactiveConfiguration.java index f287867c09..49ef68864d 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/WebReactiveConfiguration.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/WebReactiveConfiguration.java @@ -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> messageConverters; + private List> messageReaders; + + private List> 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. - *

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. + *

Use {@link #configureMessageReaders} to configure the list or + * {@link #extendMessageReaders} to add in addition to the default ones. */ - protected final List> getMessageConverters() { - if (this.messageConverters == null) { - this.messageConverters = new ArrayList<>(); - configureMessageConverters(this.messageConverters); - if (this.messageConverters.isEmpty()) { - addDefaultHttpMessageConverters(this.messageConverters); + protected final List> 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. - *

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. + *

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> converters) { + protected void configureMessageReaders(List> messageReaders) { } /** * Adds default converters that sub-classes can call from - * {@link #configureMessageConverters(List)}. + * {@link #configureMessageReaders(List)}. */ - protected final void addDefaultHttpMessageConverters(List> converters) { - List> 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> 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 HttpMessageConverter converter(Encoder encoder, Decoder 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> converters) { + protected void extendMessageReaders(List> 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. + *

Use {@link #configureMessageWriters(List)} to configure the list or + * {@link #extendMessageWriters(List)} to add in addition to the default ones. + */ + protected final List> 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. + *

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> messageWriters) { + } + /** + * Adds default converters that sub-classes can call from + * {@link #configureMessageWriters(List)}. + */ + protected final void addDefaultHttpMessageWriters(List> writers) { + List> 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> messageWriters) { + } + @Bean public ViewResolutionResultHandler viewResolutionResultHandler() { ViewResolverRegistry registry = new ViewResolverRegistry(this.applicationContext); diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageConverterArgumentResolver.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java similarity index 88% rename from spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageConverterArgumentResolver.java rename to spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java index 55a07cc426..499c8f6b93 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageConverterArgumentResolver.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java @@ -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}. * *

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> messageConverters; + private final List> 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> converters, + protected AbstractMessageReaderArgumentResolver(List> 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> getMessageConverters() { - return this.messageConverters; + public List> 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))); diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageConverterResultHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageWriterResultHandler.java similarity index 81% rename from spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageConverterResultHandler.java rename to spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageWriterResultHandler.java index ab19c26460..c42034efca 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageConverterResultHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageWriterResultHandler.java @@ -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> messageConverters; + private final List> 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> converters, + protected AbstractMessageWriterResultHandler(List> 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> getMessageConverters() { - return this.messageConverters; + public List> 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 getProducibleMediaTypes(ResolvableType elementType) { - return getMessageConverters().stream() + return getMessageWriters().stream() .filter(converter -> converter.canWrite(elementType, null)) .flatMap(converter -> converter.getWritableMediaTypes().stream()) .collect(Collectors.toList()); diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolver.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolver.java index 323e4fa250..b737a54099 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolver.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolver.java @@ -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> converters, + public HttpEntityArgumentResolver(List> 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> converters, + public HttpEntityArgumentResolver(List> messageReaders, ConversionService service, Validator validator) { - super(converters, service, validator); + super(messageReaders, service, validator); } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolver.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolver.java index 83238e6b62..39eba423a4 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolver.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolver.java @@ -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}. * *

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> converters, + public RequestBodyArgumentResolver(List> 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> converters, + public RequestBodyArgumentResolver(List> messageReaders, ConversionService service, Validator validator) { - super(converters, service, validator); + super(messageReaders, service, validator); } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java index 0d9c317797..b550ea0ba4 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java @@ -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 argumentResolvers; - private final List> messageConverters = new ArrayList<>(10); + private final List> 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> messageConverters) { - this.messageConverters.clear(); - this.messageConverters.addAll(messageConverters); + public void setMessageReaders(List> messageReaders) { + this.messageReaders.clear(); + this.messageReaders.addAll(messageReaders); } /** - * Return the configured message converters. + * Return the configured message readers. */ - public List> getMessageConverters() { - return this.messageConverters; + public List> 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())); diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandler.java index 80b62075fb..4812c9978f 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandler.java @@ -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}. * *

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> converters, + public ResponseBodyResultHandler(List> 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> converters, + public ResponseBodyResultHandler(List> messageWriters, ConversionService conversionService, RequestedContentTypeResolver contentTypeResolver) { - super(converters, conversionService, contentTypeResolver); + super(messageWriters, conversionService, contentTypeResolver); setOrder(100); } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandler.java index acbd3ecc43..fd28d33fae 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandler.java @@ -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> converters, + public ResponseEntityResultHandler(List> 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> converters, + public ResponseEntityResultHandler(List> messageWriters, ConversionService conversionService, RequestedContentTypeResolver contentTypeResolver) { - super(converters, conversionService, contentTypeResolver); + super(messageWriters, conversionService, contentTypeResolver); setOrder(0); } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/HttpMessageConverterView.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/HttpMessageWriterView.java similarity index 69% rename from spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/HttpMessageConverterView.java rename to spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/HttpMessageWriterView.java index fa6eb47f20..f9fb267151 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/HttpMessageConverterView.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/HttpMessageWriterView.java @@ -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 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). *

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 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 Mono applyConverter(Object value, MediaType contentType, ServerWebExchange exchange) { + private Mono applyMessageWriter(Object value, MediaType contentType, ServerWebExchange exchange) { if (value == null) { return Mono.empty(); } Publisher stream = Mono.just((T) value); ResolvableType type = ResolvableType.forClass(value.getClass()); ServerHttpResponse response = exchange.getResponse(); - return ((HttpMessageConverter) getConverter()).write(stream, type, contentType, response); + return ((HttpMessageWriter) getMessageWriter()).write(stream, type, contentType, response); } } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/DispatcherHandlerErrorTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/DispatcherHandlerErrorTests.java index 8ef0a67e15..5a3b39ae1a 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/DispatcherHandlerErrorTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/DispatcherHandlerErrorTests.java @@ -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()); } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/config/ViewResolverRegistryTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/config/ViewResolverRegistryTests.java index c5035730f8..95cce6339a 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/config/ViewResolverRegistryTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/config/ViewResolverRegistryTests.java @@ -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()); diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/config/WebReactiveConfigurationTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/config/WebReactiveConfigurationTests.java index 25b75c0680..83bde44174 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/config/WebReactiveConfigurationTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/config/WebReactiveConfigurationTests.java @@ -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> converters = adapter.getMessageConverters(); - assertEquals(6, converters.size()); + List> 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> converters = adapter.getMessageConverters(); - assertEquals(2, converters.size()); + List> 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> converters = handler.getMessageConverters(); - assertEquals(6, converters.size()); + List> 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> converters = handler.getMessageConverters(); - assertEquals(6, converters.size()); + List> 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> converters, Class clazz, - MediaType readMediaType, MediaType writeMediaType) { + private void assertHasMessageReader(List> 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> 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> converters) { - converters.add(new CodecHttpMessageConverter<>(new StringEncoder(), new StringDecoder())); + protected void configureMessageReaders(List> messageReaders) { + messageReaders.add(new DecoderHttpMessageReader<>(new StringDecoder())); } @Override - protected void extendMessageConverters(List> converters) { - converters.add(new CodecHttpMessageConverter<>(new Jaxb2Encoder(), new Jaxb2Decoder())); + protected void configureMessageWriters(List> messageWriters) { + messageWriters.add(new EncoderHttpMessageWriter<>(new StringEncoder())); + } + + @Override + protected void extendMessageReaders(List> messageReaders) { + messageReaders.add(new DecoderHttpMessageReader<>(new Jaxb2Decoder())); + } + + @Override + protected void extendMessageWriters(List> 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 diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolverTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolverTests.java index 37f4700259..b26ea9422c 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolverTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolverTests.java @@ -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> converters = new ArrayList<>(); - converters.add(new CodecHttpMessageConverter<>(new StringDecoder())); + List> 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); } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageConverterArgumentResolverTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageReaderArgumentResolverTests.java similarity index 94% rename from spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageConverterArgumentResolverTests.java rename to spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageReaderArgumentResolverTests.java index 3080e90ac0..99a2b0fc1b 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageConverterArgumentResolverTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageReaderArgumentResolverTests.java @@ -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> converters = new ArrayList<>(); - Arrays.asList(decoders).forEach(decoder -> converters.add(new CodecHttpMessageConverter<>(decoder))); + List> 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) { diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageConverterResultHandlerTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageWriterResultHandlerTests.java similarity index 88% rename from spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageConverterResultHandlerTests.java rename to spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageWriterResultHandlerTests.java index e177bf678c..bc51c68283 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageConverterResultHandlerTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/MessageWriterResultHandlerTests.java @@ -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 mono = createResultHandler(converter).writeBody(this.exchange, body, type, returnType(type)); + HttpMessageWriter writer = new EncoderHttpMessageWriter<>(new ByteBufferEncoder()); + Mono 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> 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> 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) { diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolverTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolverTests.java index fd5bbb007d..1b48907504 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolverTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolverTests.java @@ -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> converters = new ArrayList<>(); - converters.add(new CodecHttpMessageConverter<>(new StringDecoder())); + List> 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); } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandlerTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandlerTests.java index cfd747d5a3..52ce511667 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandlerTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandlerTests.java @@ -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: *

* @@ -86,25 +86,25 @@ public class ResponseBodyResultHandlerTests { } - private ResponseBodyResultHandler createHandler(HttpMessageConverter... converters) { - List> 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> 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 diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandlerTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandlerTests.java index 01af1f47fd..fe1b472682 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandlerTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityResultHandlerTests.java @@ -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: * * @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> 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> 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); } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/SseIntegrationTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/SseIntegrationTests.java index 03f16e865a..dc0fd016c0 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/SseIntegrationTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/SseIntegrationTests.java @@ -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> 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 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> converters) { - Encoder sseEncoder = new SseEventEncoder(Arrays.asList(new JacksonJsonEncoder())); - converters.add(new CodecHttpMessageConverter<>(sseEncoder)); - } } private static class Person { diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/HttpMessageConverterViewTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/HttpMessageWriterViewTests.java similarity index 94% rename from spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/HttpMessageConverterViewTests.java rename to spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/HttpMessageWriterViewTests.java index 5fda055a6e..b7a6b1cd67 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/HttpMessageConverterViewTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/HttpMessageWriterViewTests.java @@ -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"); diff --git a/spring-web/src/main/java/org/springframework/http/codec/SseEventEncoder.java b/spring-web/src/main/java/org/springframework/http/codec/SseEventEncoder.java index 9a6ee9aa73..923a3d271d 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/SseEventEncoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/SseEventEncoder.java @@ -90,8 +90,7 @@ public class SseEventEncoder extends AbstractEncoder { Object data = event.getData(); Flux 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) { diff --git a/spring-web/src/main/java/org/springframework/http/converter/reactive/DecoderHttpMessageReader.java b/spring-web/src/main/java/org/springframework/http/converter/reactive/DecoderHttpMessageReader.java new file mode 100644 index 0000000000..c799b8888a --- /dev/null +++ b/spring-web/src/main/java/org/springframework/http/converter/reactive/DecoderHttpMessageReader.java @@ -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 implements HttpMessageReader { + + private final Decoder decoder; + + private final List readableMediaTypes; + + + /** + * Create a {@code CodecHttpMessageConverter} with the given {@link Decoder}. + * @param decoder the decoder to use + */ + public DecoderHttpMessageReader(Decoder 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 getReadableMediaTypes() { + return this.readableMediaTypes; + } + + + @Override + public Flux 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 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); + } + +} diff --git a/spring-web/src/main/java/org/springframework/http/converter/reactive/CodecHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/reactive/EncoderHttpMessageWriter.java similarity index 59% rename from spring-web/src/main/java/org/springframework/http/converter/reactive/CodecHttpMessageConverter.java rename to spring-web/src/main/java/org/springframework/http/converter/reactive/EncoderHttpMessageWriter.java index 0dc3bd1824..5b2213dbf5 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/reactive/CodecHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/reactive/EncoderHttpMessageWriter.java @@ -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 implements HttpMessageConverter { +public class EncoderHttpMessageWriter implements HttpMessageWriter { private final Encoder encoder; - private final Decoder decoder; - - private final List readableMediaTypes; - private final List 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 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 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 encoder, Decoder decoder) { + public EncoderHttpMessageWriter(Encoder 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 getReadableMediaTypes() { - return this.readableMediaTypes; - } - @Override public List getWritableMediaTypes() { return this.writableMediaTypes; } - @Override - public Flux 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 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 write(Publisher inputStream, ResolvableType type, MediaType contentType, ReactiveHttpOutputMessage outputMessage) { diff --git a/spring-web/src/main/java/org/springframework/http/converter/reactive/HttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/reactive/HttpMessageReader.java similarity index 66% rename from spring-web/src/main/java/org/springframework/http/converter/reactive/HttpMessageConverter.java rename to spring-web/src/main/java/org/springframework/http/converter/reactive/HttpMessageReader.java index a8e1d947b5..e4f89c26b5 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/reactive/HttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/reactive/HttpMessageReader.java @@ -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 { +public interface HttpMessageReader { /** * Indicates whether the given class can be read by this converter. @@ -71,31 +72,4 @@ public interface HttpMessageConverter { */ Mono 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 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 write(Publisher inputStream, ResolvableType type, - MediaType contentType, ReactiveHttpOutputMessage outputMessage); - } diff --git a/spring-web/src/main/java/org/springframework/http/converter/reactive/HttpMessageWriter.java b/spring-web/src/main/java/org/springframework/http/converter/reactive/HttpMessageWriter.java new file mode 100644 index 0000000000..a9c82fe711 --- /dev/null +++ b/spring-web/src/main/java/org/springframework/http/converter/reactive/HttpMessageWriter.java @@ -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 { + + /** + * 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 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 write(Publisher inputStream, ResolvableType type, + MediaType contentType, ReactiveHttpOutputMessage outputMessage); + +} diff --git a/spring-web/src/main/java/org/springframework/http/converter/reactive/ResourceHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/reactive/ResourceHttpMessageWriter.java similarity index 85% rename from spring-web/src/main/java/org/springframework/http/converter/reactive/ResourceHttpMessageConverter.java rename to spring-web/src/main/java/org/springframework/http/converter/reactive/ResourceHttpMessageWriter.java index b874ca9108..0ff9a62bc2 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/reactive/ResourceHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/reactive/ResourceHttpMessageWriter.java @@ -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}. + * + *

For a Resource reader simply use {@link ResourceDecoder} wrapped with + * {@link DecoderHttpMessageReader}. * * @author Arjen Poutsma * @since 5.0 */ -public class ResourceHttpMessageConverter extends CodecHttpMessageConverter { +public class ResourceHttpMessageWriter extends EncoderHttpMessageWriter { - 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 write(Publisher inputStream, - ResolvableType type, MediaType contentType, - ReactiveHttpOutputMessage outputMessage) { + public Mono write(Publisher 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 writeContent(Resource resource, ResolvableType type, MediaType contentType, ReactiveHttpOutputMessage outputMessage) { + if (outputMessage instanceof ZeroCopyHttpOutputMessage) { Optional file = getFile(resource); if (file.isPresent()) { diff --git a/spring-web/src/main/java/org/springframework/web/client/reactive/BodyExtractor.java b/spring-web/src/main/java/org/springframework/web/client/reactive/BodyExtractor.java index e5df2c1f2c..ce51b7d487 100644 --- a/spring-web/src/main/java/org/springframework/web/client/reactive/BodyExtractor.java +++ b/spring-web/src/main/java/org/springframework/web/client/reactive/BodyExtractor.java @@ -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 { /** * 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> messageConverters); + T extract(ClientHttpResponse clientResponse, List> messageReaders); } diff --git a/spring-web/src/main/java/org/springframework/web/client/reactive/DefaultResponseErrorHandler.java b/spring-web/src/main/java/org/springframework/web/client/reactive/DefaultResponseErrorHandler.java index 5769cbd658..4651c11545 100644 --- a/spring-web/src/main/java/org/springframework/web/client/reactive/DefaultResponseErrorHandler.java +++ b/spring-web/src/main/java/org/springframework/web/client/reactive/DefaultResponseErrorHandler.java @@ -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> messageConverters) { + public void handleError(ClientHttpResponse response, List> 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); } } } diff --git a/spring-web/src/main/java/org/springframework/web/client/reactive/ResponseErrorHandler.java b/spring-web/src/main/java/org/springframework/web/client/reactive/ResponseErrorHandler.java index 734f4594af..93be8dba95 100644 --- a/spring-web/src/main/java/org/springframework/web/client/reactive/ResponseErrorHandler.java +++ b/spring-web/src/main/java/org/springframework/web/client/reactive/ResponseErrorHandler.java @@ -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> messageConverters); + void handleError(ClientHttpResponse response, List> messageReaders); } diff --git a/spring-web/src/main/java/org/springframework/web/client/reactive/ResponseExtractors.java b/spring-web/src/main/java/org/springframework/web/client/reactive/ResponseExtractors.java index 39e34619ed..69c51c87e0 100644 --- a/spring-web/src/main/java/org/springframework/web/client/reactive/ResponseExtractors.java +++ b/spring-web/src/main/java/org/springframework/web/client/reactive/ResponseExtractors.java @@ -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 ResponseExtractor> body(ResolvableType bodyType) { return (clientResponse, webClientConfig) -> (Mono) 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 ResponseExtractor> 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 ResponseExtractor>>> 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 Flux decodeResponseBody(ClientHttpResponse response, - ResolvableType responseType, List> messageConverters) { + ResolvableType responseType, List> messageReaders) { MediaType contentType = response.getHeaders().getContentType(); - HttpMessageConverter converter = resolveConverter(messageConverters, responseType, contentType); - return (Flux) converter.read(responseType, response); + HttpMessageReader reader = resolveMessageReader(messageReaders, responseType, contentType); + return (Flux) reader.read(responseType, response); } @SuppressWarnings("unchecked") protected static Mono decodeResponseBodyAsMono(ClientHttpResponse response, - ResolvableType responseType, List> messageConverters) { + ResolvableType responseType, List> messageReaders) { MediaType contentType = response.getHeaders().getContentType(); - HttpMessageConverter converter = resolveConverter(messageConverters, responseType, contentType); - return (Mono) converter.readMono(responseType, response); + HttpMessageReader reader = resolveMessageReader(messageReaders, responseType, contentType); + return (Mono) reader.readMono(responseType, response); } - protected static HttpMessageConverter resolveConverter( - List> messageConverters, ResolvableType responseType, MediaType contentType) { + protected static HttpMessageReader resolveMessageReader(List> messageReaders, + ResolvableType responseType, MediaType contentType) { - return messageConverters.stream() + return messageReaders.stream() .filter(e -> e.canRead(responseType, contentType)) .findFirst() .orElseThrow(() -> diff --git a/spring-web/src/main/java/org/springframework/web/client/reactive/WebClient.java b/spring-web/src/main/java/org/springframework/web/client/reactive/WebClient.java index 2804008b1d..8831ee949c 100644 --- a/spring-web/src/main/java/org/springframework/web/client/reactive/WebClient.java +++ b/spring-web/src/main/java/org/springframework/web/client/reactive/WebClient.java @@ -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> 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> converters) { - converters.add(converter(new ByteBufferEncoder(), new ByteBufferDecoder())); - converters.add(converter(new StringEncoder(), new StringDecoder())); - converters.add(new ResourceHttpMessageConverter()); + protected final void addDefaultHttpMessageReaders(List> 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 HttpMessageConverter converter(Encoder encoder, - Decoder 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> messageConverters) { - this.webClientConfig.setMessageConverters(messageConverters); + protected final void addDefaultHttpMessageWriters(List> 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> messageReaders) { + this.webClientConfig.setMessageReaders(messageReaders); + } + + /** + * Set the list of {@link HttpMessageWriter}s to use for encoding the HTTP + * request body. + */ + public void setMessageWriters(List> messageWrters) { + this.webClientConfig.setMessageWriters(messageWrters); } /** @@ -163,7 +179,7 @@ public final class WebClient { * Requesting from the exposed {@code Flux} will result in: *

    *
  • building the actual HTTP request using the provided {@code ClientWebRequestBuilder}
  • - *
  • encoding the HTTP request body with the configured {@code HttpMessageConverter}s
  • + *
  • encoding the HTTP request body with the configured {@code HttpMessageWriter}s
  • *
  • returning the response with a publisher of the body
  • *
*/ @@ -191,17 +207,36 @@ public final class WebClient { protected class DefaultWebClientConfig implements WebClientConfig { - private List> messageConverters; + private List> messageReaders; + + private List> messageWriters; private ResponseErrorHandler responseErrorHandler; - @Override - public List> getMessageConverters() { - return messageConverters; + + public DefaultWebClientConfig() { + this.messageReaders = new ArrayList<>(); + addDefaultHttpMessageReaders(this.messageReaders); + this.messageWriters = new ArrayList<>(); + addDefaultHttpMessageWriters(this.messageWriters); } - public void setMessageConverters(List> messageConverters) { - this.messageConverters = messageConverters; + @Override + public List> getMessageReaders() { + return this.messageReaders; + } + + public void setMessageReaders(List> messageReaders) { + this.messageReaders = messageReaders; + } + + @Override + public List> getMessageWriters() { + return this.messageWriters; + } + + public void setMessageWriters(List> 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 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 writeRequestBody(Publisher content, ResolvableType requestType, ClientHttpRequest request, - List> messageConverters) { + List> messageWriters) { MediaType contentType = request.getHeaders().getContentType(); - Optional> converter = resolveConverter(messageConverters, requestType, contentType); - if (!converter.isPresent()) { + Optional> 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> resolveConverter( - List> messageConverters, ResolvableType type, - MediaType mediaType) { - return messageConverters.stream().filter(e -> e.canWrite(type, mediaType)).findFirst(); + protected Optional> resolveWriter(List> messageWriters, + ResolvableType type, MediaType mediaType) { + + return messageWriters.stream().filter(e -> e.canWrite(type, mediaType)).findFirst(); } } diff --git a/spring-web/src/main/java/org/springframework/web/client/reactive/WebClientConfig.java b/spring-web/src/main/java/org/springframework/web/client/reactive/WebClientConfig.java index 9ab326b7d6..a36452fdb3 100644 --- a/spring-web/src/main/java/org/springframework/web/client/reactive/WebClientConfig.java +++ b/spring-web/src/main/java/org/springframework/web/client/reactive/WebClientConfig.java @@ -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> getMessageConverters(); + List> getMessageReaders(); + + /** + * Return the message writers that can help encode the HTTP request body + */ + List> getMessageWriters(); /** * Return the configured {@link ResponseErrorHandler} diff --git a/spring-web/src/main/java/org/springframework/web/client/reactive/WebClientErrorException.java b/spring-web/src/main/java/org/springframework/web/client/reactive/WebClientErrorException.java index 40109daa4d..1b00e24d6d 100644 --- a/spring-web/src/main/java/org/springframework/web/client/reactive/WebClientErrorException.java +++ b/spring-web/src/main/java/org/springframework/web/client/reactive/WebClientErrorException.java @@ -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> converters) { - super(initMessage(response), response, converters); + public WebClientErrorException(ClientHttpResponse response, List> messageReaders) { + super(initMessage(response), response, messageReaders); } private static String initMessage(ClientHttpResponse response) { diff --git a/spring-web/src/main/java/org/springframework/web/client/reactive/WebClientResponseException.java b/spring-web/src/main/java/org/springframework/web/client/reactive/WebClientResponseException.java index abf5d31f79..18e70a3d90 100644 --- a/spring-web/src/main/java/org/springframework/web/client/reactive/WebClientResponseException.java +++ b/spring-web/src/main/java/org/springframework/web/client/reactive/WebClientResponseException.java @@ -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> messageConverters; + private final List> 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> messageConverters) { + List> messageReaders) { super(message); this.clientResponse = clientResponse; - this.messageConverters = messageConverters; + this.messageReaders = messageReaders; } @@ -76,6 +76,6 @@ public class WebClientResponseException extends WebClientException { * */ public T getResponseBody(BodyExtractor extractor) { - return extractor.extract(this.clientResponse, this.messageConverters); + return extractor.extract(this.clientResponse, this.messageReaders); } } diff --git a/spring-web/src/main/java/org/springframework/web/client/reactive/WebServerErrorException.java b/spring-web/src/main/java/org/springframework/web/client/reactive/WebServerErrorException.java index 1d5a02805a..d7d8d9617d 100644 --- a/spring-web/src/main/java/org/springframework/web/client/reactive/WebServerErrorException.java +++ b/spring-web/src/main/java/org/springframework/web/client/reactive/WebServerErrorException.java @@ -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> converters) { - super(initMessage(response), response, converters); + public WebServerErrorException(ClientHttpResponse response, List> messageReaders) { + super(initMessage(response), response, messageReaders); } private static String initMessage(ClientHttpResponse response) { diff --git a/spring-web/src/main/java/org/springframework/web/client/reactive/support/RxJava1ResponseExtractors.java b/spring-web/src/main/java/org/springframework/web/client/reactive/support/RxJava1ResponseExtractors.java index 842c8fd1c4..5079c67cbb 100644 --- a/spring-web/src/main/java/org/springframework/web/client/reactive/support/RxJava1ResponseExtractors.java +++ b/spring-web/src/main/java/org/springframework/web/client/reactive/support/RxJava1ResponseExtractors.java @@ -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) 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 Flux decodeResponseBody(ClientHttpResponse response, - ResolvableType responseType, List> messageConverters) { + ResolvableType responseType, List> messageReaders) { MediaType contentType = response.getHeaders().getContentType(); - HttpMessageConverter converter = resolveConverter(messageConverters, responseType, contentType); + HttpMessageReader converter = resolveMessageReader(messageReaders, responseType, contentType); return (Flux) converter.read(responseType, response); } @SuppressWarnings("unchecked") protected static Mono decodeResponseBodyAsMono(ClientHttpResponse response, - ResolvableType responseType, List> messageConverters) { + ResolvableType responseType, List> messageReaders) { MediaType contentType = response.getHeaders().getContentType(); - HttpMessageConverter converter = resolveConverter(messageConverters, responseType, contentType); + HttpMessageReader converter = resolveMessageReader(messageReaders, responseType, contentType); return (Mono) converter.readMono(responseType, response); } - protected static HttpMessageConverter resolveConverter( - List> messageConverters, ResolvableType responseType, MediaType contentType) { + protected static HttpMessageReader resolveMessageReader(List> messageReaders, + ResolvableType responseType, MediaType contentType) { - return messageConverters.stream() + return messageReaders.stream() .filter(e -> e.canRead(responseType, contentType)) .findFirst() .orElseThrow(() -> diff --git a/spring-web/src/test/java/org/springframework/web/client/reactive/DefaultResponseErrorHandlerTests.java b/spring-web/src/test/java/org/springframework/web/client/reactive/DefaultResponseErrorHandlerTests.java index e510d169ec..68cedd6725 100644 --- a/spring-web/src/test/java/org/springframework/web/client/reactive/DefaultResponseErrorHandlerTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/reactive/DefaultResponseErrorHandlerTests.java @@ -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> messageConverters; + private List> 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) { diff --git a/spring-web/src/test/java/org/springframework/web/client/reactive/ResponseExtractorsTests.java b/spring-web/src/test/java/org/springframework/web/client/reactive/ResponseExtractorsTests.java index 15ea0f7fa0..62c9ec2911 100644 --- a/spring-web/src/test/java/org/springframework/web/client/reactive/ResponseExtractorsTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/reactive/ResponseExtractorsTests.java @@ -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> messageConverters; + private List> 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)); }