diff --git a/spring-web/src/main/java/org/springframework/http/codec/AbstractServerHttpMessageReader.java b/spring-web/src/main/java/org/springframework/http/codec/AbstractServerHttpMessageReader.java deleted file mode 100644 index 36cfb37732a..00000000000 --- a/spring-web/src/main/java/org/springframework/http/codec/AbstractServerHttpMessageReader.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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.codec; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.core.MethodParameter; -import org.springframework.core.ResolvableType; -import org.springframework.http.MediaType; -import org.springframework.http.ReactiveHttpInputMessage; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; - -/** - * {@link HttpMessageReader} wrapper that implements {@link ServerHttpMessageReader} in order - * to allow providing hints to the nested {@code reader} or setting the response status for - * example, by implementing {@link #resolveReadHints(ResolvableType, ResolvableType, ServerHttpRequest)}. - * - * @author Sebastien Deleuze - * @since 5.0 - */ -public abstract class AbstractServerHttpMessageReader implements ServerHttpMessageReader { - - private HttpMessageReader reader; - - - public AbstractServerHttpMessageReader(HttpMessageReader reader) { - this.reader = reader; - } - - @Override - public boolean canRead(ResolvableType elementType, MediaType mediaType) { - return this.reader.canRead(elementType, mediaType); - } - - @Override - public Flux read(ResolvableType elementType, ReactiveHttpInputMessage inputMessage, Map hints) { - return this.reader.read(elementType, inputMessage, hints); - } - - @Override - public Mono readMono(ResolvableType elementType, ReactiveHttpInputMessage inputMessage, Map hints) { - return this.reader.readMono(elementType, inputMessage, hints); - } - - @Override - public List getReadableMediaTypes() { - return this.reader.getReadableMediaTypes(); - } - - @Override - public Flux read(ResolvableType streamType, ResolvableType elementType, - ServerHttpRequest request, ServerHttpResponse response, Map hints) { - - Map mergedHints = new HashMap<>(hints); - mergedHints.putAll(resolveReadHints(streamType, elementType, request)); - - return (this.reader instanceof ServerHttpMessageReader ? - ((ServerHttpMessageReader)this.reader).read(streamType, elementType, request, response, mergedHints) : - this.read(elementType, request, mergedHints)); - } - - @Override - public Mono readMono(ResolvableType streamType, ResolvableType elementType, - ServerHttpRequest request, ServerHttpResponse response, Map hints) { - - Map mergedHints = new HashMap<>(hints); - mergedHints.putAll(resolveReadHints(streamType, elementType, request)); - - return (this.reader instanceof ServerHttpMessageReader ? - ((ServerHttpMessageReader)this.reader).readMono(streamType, elementType, request, response, mergedHints) : - this.readMono(elementType, request, mergedHints)); - } - - /** - * Invoked before reading the request to resolve hints by - * {@link #read(ResolvableType, ResolvableType, ServerHttpRequest, ServerHttpResponse, Map)}. - * - * @param streamType the original type used for the method return value. For annotation - * based controllers, the {@link MethodParameter} is available via {@link ResolvableType#getSource()}. - * @param elementType the stream element type to process - * @param request the current HTTP request - * @return Additional information about how to write the body - */ - protected abstract Map resolveReadHints(ResolvableType streamType, - ResolvableType elementType, ServerHttpRequest request); - -} diff --git a/spring-web/src/main/java/org/springframework/http/codec/AbstractServerHttpMessageWriter.java b/spring-web/src/main/java/org/springframework/http/codec/AbstractServerHttpMessageWriter.java deleted file mode 100644 index 54ddec8e504..00000000000 --- a/spring-web/src/main/java/org/springframework/http/codec/AbstractServerHttpMessageWriter.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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.codec; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.reactivestreams.Publisher; -import reactor.core.publisher.Mono; - -import org.springframework.core.MethodParameter; -import org.springframework.core.ResolvableType; -import org.springframework.http.MediaType; -import org.springframework.http.ReactiveHttpOutputMessage; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; - -/** - * {@link HttpMessageWriter} wrapper that implements {@link ServerHttpMessageWriter} in order - * to allow providing hints to the nested {@code writer} or setting the response status for - * example, by implementing {@link #resolveWriteHints(ResolvableType, ResolvableType, MediaType, ServerHttpRequest)}. - * - * @author Sebastien Deleuze - * @since 5.0 - */ -public abstract class AbstractServerHttpMessageWriter implements ServerHttpMessageWriter { - - private HttpMessageWriter writer; - - - public AbstractServerHttpMessageWriter(HttpMessageWriter writer) { - this.writer = writer; - } - - - @Override - public boolean canWrite(ResolvableType elementType, MediaType mediaType) { - return this.writer.canWrite(elementType, mediaType); - } - - @Override - public List getWritableMediaTypes() { - return this.writer.getWritableMediaTypes(); - } - - @Override - public Mono write(Publisher inputStream, ResolvableType elementType, - MediaType mediaType, ReactiveHttpOutputMessage outputMessage, Map hints) { - return this.writer.write(inputStream, elementType, mediaType, outputMessage, hints); - } - - @Override - public Mono write(Publisher inputStream, ResolvableType streamType, ResolvableType elementType, - MediaType mediaType, ServerHttpRequest request, ServerHttpResponse response, Map hints) { - - Map mergedHints = new HashMap<>(hints); - mergedHints.putAll(resolveWriteHints(streamType, elementType, mediaType, request)); - return (this.writer instanceof ServerHttpMessageWriter ? - ((ServerHttpMessageWriter)this.writer).write(inputStream, streamType, - elementType, mediaType, request, response, mergedHints) : - this.writer.write(inputStream, elementType, mediaType, response, mergedHints)); - } - - /** - * Invoked before writing the response to resolve hints by - * {@link #write(Publisher, ResolvableType, ResolvableType, MediaType, ServerHttpRequest, ServerHttpResponse, Map)}. - * @param streamType the original type used for the method return value. For annotation - * based controllers, the {@link MethodParameter} is available via {@link ResolvableType#getSource()}. - * @param elementType the stream element type to process - * @param mediaType 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 request the current HTTP request - * @return additional information about how to write the body - */ - protected abstract Map resolveWriteHints(ResolvableType streamType, ResolvableType elementType, - MediaType mediaType, ServerHttpRequest request); - -} diff --git a/spring-web/src/main/java/org/springframework/http/codec/DecoderHttpMessageReader.java b/spring-web/src/main/java/org/springframework/http/codec/DecoderHttpMessageReader.java index 962420b170a..e7812439b4c 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/DecoderHttpMessageReader.java +++ b/spring-web/src/main/java/org/springframework/http/codec/DecoderHttpMessageReader.java @@ -17,6 +17,7 @@ package org.springframework.http.codec; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -28,6 +29,8 @@ import org.springframework.core.codec.Decoder; import org.springframework.http.HttpMessage; import org.springframework.http.MediaType; import org.springframework.http.ReactiveHttpInputMessage; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.util.Assert; /** @@ -38,7 +41,7 @@ import org.springframework.util.Assert; * @author Rossen Stoyanchev * @since 5.0 */ -public class DecoderHttpMessageReader implements HttpMessageReader { +public class DecoderHttpMessageReader implements ServerHttpMessageReader { private final Decoder decoder; @@ -94,4 +97,39 @@ public class DecoderHttpMessageReader implements HttpMessageReader { return (contentType != null ? contentType : MediaType.APPLICATION_OCTET_STREAM); } + + // ServerHttpMessageReader... + + @Override + public Flux read(ResolvableType streamType, ResolvableType elementType, + ServerHttpRequest request, ServerHttpResponse response, Map hints) { + + Map allHints = new HashMap<>(4); + allHints.putAll(resolveReadHints(streamType, elementType, request, response)); + allHints.putAll(hints); + + return read(elementType, request, allHints); + } + + @Override + public Mono readMono(ResolvableType streamType, ResolvableType elementType, + ServerHttpRequest request, ServerHttpResponse response, Map hints) { + + Map allHints = new HashMap<>(4); + allHints.putAll(resolveReadHints(streamType, elementType, request, response)); + allHints.putAll(hints); + + return readMono(elementType, request, allHints); + } + + /** + * Resolve hints to pass to the decoder, e.g. by checking for annotations + * on a controller method parameter or checking the server request. + */ + protected Map resolveReadHints(ResolvableType streamType, + ResolvableType elementType, ServerHttpRequest request, ServerHttpResponse response) { + + return Collections.emptyMap(); + } + } diff --git a/spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java b/spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java index da6ac6513b3..3ae2f9ddacb 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java +++ b/spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java @@ -16,6 +16,8 @@ package org.springframework.http.codec; +import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -29,6 +31,8 @@ import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ReactiveHttpOutputMessage; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.util.Assert; import static org.springframework.core.codec.AbstractEncoder.FLUSHING_STRATEGY_HINT; @@ -42,7 +46,7 @@ import static org.springframework.core.codec.AbstractEncoder.FlushingStrategy.AF * @author Rossen Stoyanchev * @since 5.0 */ -public class EncoderHttpMessageWriter implements HttpMessageWriter { +public class EncoderHttpMessageWriter implements ServerHttpMessageWriter { private final Encoder encoder; @@ -119,4 +123,29 @@ public class EncoderHttpMessageWriter implements HttpMessageWriter { return main; } + + // ServerHttpMessageWriter... + + @Override + public Mono write(Publisher inputStream, ResolvableType streamType, + ResolvableType elementType, MediaType mediaType, ServerHttpRequest request, + ServerHttpResponse response, Map hints) { + + Map allHints = new HashMap<>(); + allHints.putAll(resolveWriteHints(streamType, elementType, mediaType, request, response)); + allHints.putAll(hints); + + return write(inputStream, elementType, mediaType, response, allHints); + } + + /** + * Resolve hints to pass to the encoder, e.g. by checking for annotations + * on a controller method parameter or checking the server request. + */ + protected Map resolveWriteHints(ResolvableType streamType, ResolvableType elementType, + MediaType mediaType, ServerHttpRequest request, ServerHttpResponse response) { + + return Collections.emptyMap(); + } + } diff --git a/spring-web/src/main/java/org/springframework/http/codec/Jackson2ServerHttpMessageReader.java b/spring-web/src/main/java/org/springframework/http/codec/Jackson2ServerHttpMessageReader.java index eefad0e5574..50da50349a4 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/Jackson2ServerHttpMessageReader.java +++ b/spring-web/src/main/java/org/springframework/http/codec/Jackson2ServerHttpMessageReader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -23,8 +23,10 @@ import com.fasterxml.jackson.annotation.JsonView; import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; +import org.springframework.core.codec.Decoder; import org.springframework.http.codec.json.AbstractJackson2Codec; import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; /** * {@link ServerHttpMessageReader} that resolves those annotation or request based Jackson 2 hints: @@ -33,18 +35,21 @@ import org.springframework.http.server.reactive.ServerHttpRequest; * * * @author Sebastien Deleuze + * @author Rossen Stoyanchev * @since 5.0 * @see com.fasterxml.jackson.annotation.JsonView */ -public class Jackson2ServerHttpMessageReader extends AbstractServerHttpMessageReader { +public class Jackson2ServerHttpMessageReader extends DecoderHttpMessageReader { - public Jackson2ServerHttpMessageReader(HttpMessageReader reader) { - super(reader); + + public Jackson2ServerHttpMessageReader(Decoder decoder) { + super(decoder); } + @Override protected Map resolveReadHints(ResolvableType streamType, - ResolvableType elementType, ServerHttpRequest request) { + ResolvableType elementType, ServerHttpRequest request, ServerHttpResponse response) { Object source = streamType.getSource(); MethodParameter parameter = (source instanceof MethodParameter ? (MethodParameter)source : null); diff --git a/spring-web/src/main/java/org/springframework/http/codec/Jackson2ServerHttpMessageWriter.java b/spring-web/src/main/java/org/springframework/http/codec/Jackson2ServerHttpMessageWriter.java index a00d358a946..c7eedce36cf 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/Jackson2ServerHttpMessageWriter.java +++ b/spring-web/src/main/java/org/springframework/http/codec/Jackson2ServerHttpMessageWriter.java @@ -44,38 +44,13 @@ import org.springframework.http.server.reactive.ServerHttpResponse; * @since 5.0 * @see com.fasterxml.jackson.annotation.JsonView */ -public class Jackson2ServerHttpMessageWriter extends AbstractServerHttpMessageWriter { +public class Jackson2ServerHttpMessageWriter extends EncoderHttpMessageWriter { public Jackson2ServerHttpMessageWriter(Encoder encoder) { - super(new EncoderHttpMessageWriter<>(encoder)); + super(encoder); } - public Jackson2ServerHttpMessageWriter(HttpMessageWriter writer) { - super(writer); - } - - - @Override - protected Map resolveWriteHints(ResolvableType streamType, - ResolvableType elementType, MediaType mediaType, ServerHttpRequest request) { - - Map hints = new HashMap<>(); - Object source = streamType.getSource(); - MethodParameter returnValue = (source instanceof MethodParameter ? (MethodParameter)source : null); - if (returnValue != null) { - JsonView annotation = returnValue.getMethodAnnotation(JsonView.class); - if (annotation != null) { - Class[] classes = annotation.value(); - if (classes.length != 1) { - throw new IllegalArgumentException( - "@JsonView only supported for write hints with exactly 1 class argument: " + returnValue); - } - hints.put(AbstractJackson2Codec.JSON_VIEW_HINT, classes[0]); - } - } - return hints; - } @Override public Mono write(Publisher inputStream, ResolvableType elementType, MediaType mediaType, @@ -100,4 +75,26 @@ public class Jackson2ServerHttpMessageWriter extends AbstractServerHttpMessageWr } return super.write(inputStream, streamType, elementType, mediaType, request, response, hints); } + + @Override + protected Map resolveWriteHints(ResolvableType streamType, ResolvableType elementType, + MediaType mediaType, ServerHttpRequest request, ServerHttpResponse response) { + + Map hints = new HashMap<>(); + Object source = streamType.getSource(); + MethodParameter returnValue = (source instanceof MethodParameter ? (MethodParameter)source : null); + if (returnValue != null) { + JsonView annotation = returnValue.getMethodAnnotation(JsonView.class); + if (annotation != null) { + Class[] classes = annotation.value(); + if (classes.length != 1) { + throw new IllegalArgumentException( + "@JsonView only supported for write hints with exactly 1 class argument: " + returnValue); + } + hints.put(AbstractJackson2Codec.JSON_VIEW_HINT, classes[0]); + } + } + return hints; + } + } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java b/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java index 112b5d6dc75..5d89ea0e265 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java @@ -491,12 +491,8 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware { Jackson2JsonEncoder encoder = new Jackson2JsonEncoder(); writers.add(new Jackson2ServerHttpMessageWriter(encoder)); sseDataEncoders.add(encoder); - HttpMessageWriter writer = new ServerSentEventHttpMessageWriter(sseDataEncoders); - writers.add(new Jackson2ServerHttpMessageWriter(writer)); - } - else { - writers.add(new ServerSentEventHttpMessageWriter(sseDataEncoders)); } + writers.add(new ServerSentEventHttpMessageWriter(sseDataEncoders)); } /**