Browse Source

Make ServerHttpMessageReader/Writer more powerful and flexible

This commit makes it possible, in addition to provide hints, to
perform additional operations with the request and the response
at ServerHttpMessageReader/Writer level.

AbstractServerHttpMessageReader/Writer now provide
convenient beforeRead/beforeWrite abstract methods for such need.

Issue: SPR-14557
pull/1175/head
Sebastien Deleuze 9 years ago
parent
commit
857e77eec2
  1. 23
      spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java
  2. 21
      spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageWriterResultHandler.java
  3. 56
      spring-web/src/main/java/org/springframework/http/codec/AbstractServerHttpMessageReader.java
  4. 50
      spring-web/src/main/java/org/springframework/http/codec/AbstractServerHttpMessageWriter.java
  5. 6
      spring-web/src/main/java/org/springframework/http/codec/Jackson2ServerHttpMessageReader.java
  6. 15
      spring-web/src/main/java/org/springframework/http/codec/Jackson2ServerHttpMessageWriter.java
  7. 38
      spring-web/src/main/java/org/springframework/http/codec/ServerHttpMessageReader.java
  8. 21
      spring-web/src/main/java/org/springframework/http/codec/ServerHttpMessageWriter.java

23
spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java

@ -18,7 +18,6 @@ package org.springframework.web.reactive.result.method.annotation;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -35,6 +34,7 @@ import org.springframework.http.MediaType;
import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.ServerHttpMessageReader; import org.springframework.http.codec.ServerHttpMessageReader;
import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.validation.BeanPropertyBindingResult; import org.springframework.validation.BeanPropertyBindingResult;
@ -126,6 +126,7 @@ public abstract class AbstractMessageReaderArgumentResolver {
} }
ServerHttpRequest request = exchange.getRequest(); ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
MediaType mediaType = request.getHeaders().getContentType(); MediaType mediaType = request.getHeaders().getContentType();
if (mediaType == null) { if (mediaType == null) {
mediaType = MediaType.APPLICATION_OCTET_STREAM; mediaType = MediaType.APPLICATION_OCTET_STREAM;
@ -133,15 +134,14 @@ public abstract class AbstractMessageReaderArgumentResolver {
for (HttpMessageReader<?> reader : getMessageReaders()) { for (HttpMessageReader<?> reader : getMessageReaders()) {
Map<String, Object> hints = (reader instanceof ServerHttpMessageReader ? if (reader.canRead(elementType, mediaType)) {
((ServerHttpMessageReader<?>)reader).resolveReadHints(bodyType, elementType,
mediaType, exchange.getRequest()) : Collections.emptyMap());
if (reader.canRead(elementType, mediaType, hints)) {
if (adapter != null && adapter.getDescriptor().isMultiValue()) { if (adapter != null && adapter.getDescriptor().isMultiValue()) {
Flux<?> flux = reader.read(elementType, request, hints) Flux<?> flux = (reader instanceof ServerHttpMessageReader ?
.onErrorResumeWith(ex -> Flux.error(getReadError(ex, bodyParameter))); ((ServerHttpMessageReader<?>)reader).read(bodyType, elementType,
request, response, Collections.emptyMap()) :
reader.read(elementType, request, Collections.emptyMap())
.onErrorResumeWith(ex -> Flux.error(getReadError(ex, bodyParameter))));
if (checkRequired(adapter, isBodyRequired)) { if (checkRequired(adapter, isBodyRequired)) {
flux = flux.switchIfEmpty(Flux.error(getRequiredBodyError(bodyParameter))); flux = flux.switchIfEmpty(Flux.error(getRequiredBodyError(bodyParameter)));
} }
@ -151,8 +151,11 @@ public abstract class AbstractMessageReaderArgumentResolver {
return Mono.just(adapter.fromPublisher(flux)); return Mono.just(adapter.fromPublisher(flux));
} }
else { else {
Mono<?> mono = reader.readMono(elementType, request, hints) Mono<?> mono = (reader instanceof ServerHttpMessageReader ?
.otherwise(ex -> Mono.error(getReadError(ex, bodyParameter))); ((ServerHttpMessageReader<?>)reader).readMono(bodyType, elementType,
request, response, Collections.emptyMap()) :
reader.readMono(elementType, request, Collections.emptyMap())
.otherwise(ex -> Mono.error(getReadError(ex, bodyParameter))));
if (checkRequired(adapter, isBodyRequired)) { if (checkRequired(adapter, isBodyRequired)) {
mono = mono.otherwiseIfEmpty(Mono.error(getRequiredBodyError(bodyParameter))); mono = mono.otherwiseIfEmpty(Mono.error(getRequiredBodyError(bodyParameter)));
} }

21
spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageWriterResultHandler.java

@ -17,7 +17,6 @@ package org.springframework.web.reactive.result.method.annotation;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
@ -30,6 +29,7 @@ import org.springframework.core.ResolvableType;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.codec.HttpMessageWriter; import org.springframework.http.codec.HttpMessageWriter;
import org.springframework.http.codec.ServerHttpMessageWriter; import org.springframework.http.codec.ServerHttpMessageWriter;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.web.reactive.accept.RequestedContentTypeResolver; import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
@ -119,18 +119,17 @@ public abstract class AbstractMessageWriterResultHandler extends ContentNegotiat
"No converter for return value type: " + elementType)); "No converter for return value type: " + elementType));
} }
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
MediaType bestMediaType = selectMediaType(exchange, producibleTypes); MediaType bestMediaType = selectMediaType(exchange, producibleTypes);
if (bestMediaType != null) { if (bestMediaType != null) {
for (HttpMessageWriter<?> messageWriter : getMessageWriters()) { for (HttpMessageWriter<?> messageWriter : getMessageWriters()) {
Map<String, Object> hints = (messageWriter instanceof ServerHttpMessageWriter ? if (messageWriter.canWrite(elementType, bestMediaType)) {
((ServerHttpMessageWriter<?>)messageWriter).resolveWriteHints(bodyType, elementType, return (messageWriter instanceof ServerHttpMessageWriter ?
bestMediaType, exchange.getRequest()) : Collections.emptyMap()); ((ServerHttpMessageWriter<?>)messageWriter).write((Publisher) publisher,
if (messageWriter.canWrite(elementType, bestMediaType, hints)) { bodyType, elementType, bestMediaType, request, response, Collections.emptyMap()) :
messageWriter.write((Publisher) publisher, elementType,
ServerHttpResponse response = exchange.getResponse(); bestMediaType, response, Collections.emptyMap()));
return messageWriter.write((Publisher) publisher, elementType,
bestMediaType, response, hints);
} }
} }
} }
@ -140,7 +139,7 @@ public abstract class AbstractMessageWriterResultHandler extends ContentNegotiat
private List<MediaType> getProducibleMediaTypes(ResolvableType elementType) { private List<MediaType> getProducibleMediaTypes(ResolvableType elementType) {
return getMessageWriters().stream() return getMessageWriters().stream()
.filter(converter -> converter.canWrite(elementType, null, Collections.emptyMap())) .filter(converter -> converter.canWrite(elementType, null))
.flatMap(converter -> converter.getWritableMediaTypes().stream()) .flatMap(converter -> converter.getWritableMediaTypes().stream())
.collect(Collectors.toList()); .collect(Collectors.toList());
} }

56
spring-web/src/main/java/org/springframework/http/codec/AbstractServerHttpMessageReader.java

@ -28,6 +28,7 @@ import org.springframework.core.ResolvableType;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ReactiveHttpInputMessage; import org.springframework.http.ReactiveHttpInputMessage;
import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
/** /**
* {@link HttpMessageReader} wrapper that implements {@link ServerHttpMessageReader} in order * {@link HttpMessageReader} wrapper that implements {@link ServerHttpMessageReader} in order
@ -46,8 +47,8 @@ public abstract class AbstractServerHttpMessageReader<T> implements ServerHttpMe
} }
@Override @Override
public boolean canRead(ResolvableType elementType, MediaType mediaType, Map<String, Object> hints) { public boolean canRead(ResolvableType elementType, MediaType mediaType) {
return this.reader.canRead(elementType, mediaType, hints); return this.reader.canRead(elementType, mediaType);
} }
@Override @Override
@ -66,29 +67,42 @@ public abstract class AbstractServerHttpMessageReader<T> implements ServerHttpMe
} }
@Override @Override
public final Map<String, Object> resolveReadHints(ResolvableType streamType, public Flux<T> read(ResolvableType streamType, ResolvableType elementType,
ResolvableType elementType, MediaType mediaType, ServerHttpRequest request) { ServerHttpRequest request, ServerHttpResponse response, Map<String, Object> hints) {
Map<String, Object> hints = new HashMap<>(); Map<String, Object> mergedHints = new HashMap<>(hints);
if (this.reader instanceof ServerHttpMessageReader) { mergedHints.putAll(beforeRead(streamType, elementType, request, response));
hints.putAll(((ServerHttpMessageReader<T>)this.reader).resolveReadHints(streamType, elementType, mediaType, request));
} return (this.reader instanceof ServerHttpMessageReader ?
hints.putAll(resolveReadHintsInternal(streamType, elementType, mediaType, request)); ((ServerHttpMessageReader<T>)this.reader).read(streamType, elementType, request, response, mergedHints) :
return hints; this.read(elementType, request, mergedHints));
}
@Override
public Mono<T> readMono(ResolvableType streamType, ResolvableType elementType,
ServerHttpRequest request, ServerHttpResponse response, Map<String, Object> hints) {
Map<String, Object> mergedHints = new HashMap<>(hints);
mergedHints.putAll(beforeRead(streamType, elementType, request, response));
return (this.reader instanceof ServerHttpMessageReader ?
((ServerHttpMessageReader<T>)this.reader).readMono(streamType, elementType, request, response, mergedHints) :
this.readMono(elementType, request, mergedHints));
} }
/** /**
* Abstract method that returns hints which can be used to customize how the body should be read. * Invoked before reading the request by
* Invoked from {@link #resolveReadHints}. * {@link #read(ResolvableType, ResolvableType, ServerHttpRequest, ServerHttpResponse, Map)}
* @param streamType the original type used in the method parameter. For annotation *
* @param streamType the original type used for the method return value. For annotation
* based controllers, the {@link MethodParameter} is available via {@link ResolvableType#getSource()}. * based controllers, the {@link MethodParameter} is available via {@link ResolvableType#getSource()}.
* @param elementType the stream element type to return * Can be {@code null}.
* @param mediaType the media type to read, can be {@code null} if not specified. * @param elementType the stream element type to process
* Typically the value of a {@code Content-Type} header. * @param request the current HTTP request, can be {@code null}
* @param request the current HTTP request * @param response the current HTTP response, can be {@code null}
* @return Additional information about how to read the body * @return Additional information about how to write the body
*/ */
protected abstract Map<String, Object> resolveReadHintsInternal(ResolvableType streamType, protected abstract Map<String, Object> beforeRead(ResolvableType streamType,
ResolvableType elementType, MediaType mediaType, ServerHttpRequest request); ResolvableType elementType, ServerHttpRequest request, ServerHttpResponse response);
} }

50
spring-web/src/main/java/org/springframework/http/codec/AbstractServerHttpMessageWriter.java

@ -28,10 +28,13 @@ import org.springframework.core.ResolvableType;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ReactiveHttpOutputMessage; import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
/** /**
* {@link HttpMessageWriter} wrapper that implements {@link ServerHttpMessageWriter} in order * {@link HttpMessageWriter} wrapper that implements {@link ServerHttpMessageWriter} in order
* to allow providing hints. * to allow providing hints to the nested {@code writer} or setting the response status, for
* example, by implementing {@link #beforeWrite(ResolvableType, ResolvableType, MediaType, ServerHttpRequest, ServerHttpResponse)}
*
* *
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @since 5.0 * @since 5.0
@ -46,15 +49,8 @@ public abstract class AbstractServerHttpMessageWriter<T> implements ServerHttpMe
} }
@Override @Override
public boolean canWrite(ResolvableType elementType, MediaType mediaType, Map<String, Object> hints) { public boolean canWrite(ResolvableType elementType, MediaType mediaType) {
return this.writer.canWrite(elementType, mediaType, hints); return this.writer.canWrite(elementType, mediaType);
}
@Override
public Mono<Void> write(Publisher<? extends T> inputStream, ResolvableType elementType,
MediaType mediaType, ReactiveHttpOutputMessage outputMessage, Map<String, Object> hints) {
return this.writer.write(inputStream, elementType, mediaType, outputMessage, hints);
} }
@Override @Override
@ -63,29 +59,37 @@ public abstract class AbstractServerHttpMessageWriter<T> implements ServerHttpMe
} }
@Override @Override
public final Map<String, Object> resolveWriteHints(ResolvableType streamType, public Mono<Void> write(Publisher<? extends T> inputStream, ResolvableType elementType,
ResolvableType elementType, MediaType mediaType, ServerHttpRequest request) { MediaType mediaType, ReactiveHttpOutputMessage outputMessage, Map<String, Object> hints) {
return this.writer.write(inputStream, elementType, mediaType, outputMessage, hints);
}
Map<String, Object> hints = new HashMap<>(); @Override
if (this.writer instanceof ServerHttpMessageWriter) { public Mono<Void> write(Publisher<? extends T> inputStream, ResolvableType streamType, ResolvableType elementType,
hints.putAll(((ServerHttpMessageWriter<T>)this.writer).resolveWriteHints(streamType, elementType, mediaType, request)); MediaType mediaType, ServerHttpRequest request, ServerHttpResponse response, Map<String, Object> hints) {
}
hints.putAll(resolveWriteHintsInternal(streamType, elementType, mediaType, request)); Map<String, Object> mergedHints = new HashMap<>(hints);
return hints; mergedHints.putAll(beforeWrite(streamType, elementType, mediaType, request, response));
return (this.writer instanceof ServerHttpMessageWriter ?
((ServerHttpMessageWriter<T>)this.writer).write(inputStream, streamType, elementType, mediaType, request, response, mergedHints) :
this.writer.write(inputStream, elementType, mediaType, response, mergedHints));
} }
/** /**
* Abstract method that returns hints which can be used to customize how the body should be written. * Invoked before writing the response by
* Invoked from {@link #resolveWriteHints}. * {@link #write(Publisher, ResolvableType, ResolvableType, MediaType, ServerHttpRequest, ServerHttpResponse, Map)}.
*
* @param streamType the original type used for the method return value. For annotation * @param streamType the original type used for the method return value. For annotation
* based controllers, the {@link MethodParameter} is available via {@link ResolvableType#getSource()}. * based controllers, the {@link MethodParameter} is available via {@link ResolvableType#getSource()}.
* Can be {@code null}.
* @param elementType the stream element type to process * @param elementType the stream element type to process
* @param mediaType the content type to use when writing. May be {@code null} to * @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. * indicate that the default content type of the converter must be used.
* @param request the current HTTP request * @param request the current HTTP request, can be {@code null}
* @param response the current HTTP response, can be {@code null}
* @return Additional information about how to write the body * @return Additional information about how to write the body
*/ */
protected abstract Map<String, Object> resolveWriteHintsInternal(ResolvableType streamType, protected abstract Map<String, Object> beforeWrite(ResolvableType streamType, ResolvableType elementType,
ResolvableType elementType, MediaType mediaType, ServerHttpRequest request); MediaType mediaType, ServerHttpRequest request, ServerHttpResponse response);
} }

6
spring-web/src/main/java/org/springframework/http/codec/Jackson2ServerHttpMessageReader.java

@ -23,9 +23,9 @@ import com.fasterxml.jackson.annotation.JsonView;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.http.MediaType;
import org.springframework.http.codec.json.AbstractJackson2Codec; import org.springframework.http.codec.json.AbstractJackson2Codec;
import org.springframework.http.server.reactive.ServerHttpRequest; 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: * {@link ServerHttpMessageReader} that resolves those annotation or request based Jackson 2 hints:
@ -44,8 +44,8 @@ public class Jackson2ServerHttpMessageReader extends AbstractServerHttpMessageRe
} }
@Override @Override
protected Map<String, Object> resolveReadHintsInternal(ResolvableType streamType, protected Map<String, Object> beforeRead(ResolvableType streamType,
ResolvableType elementType, MediaType mediaType, ServerHttpRequest request) { ResolvableType elementType, ServerHttpRequest request, ServerHttpResponse response) {
Object source = streamType.getSource(); Object source = streamType.getSource();
MethodParameter parameter = (source instanceof MethodParameter ? (MethodParameter)source : null); MethodParameter parameter = (source instanceof MethodParameter ? (MethodParameter)source : null);

15
spring-web/src/main/java/org/springframework/http/codec/Jackson2ServerHttpMessageWriter.java

@ -16,16 +16,20 @@
package org.springframework.http.codec; package org.springframework.http.codec;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import com.fasterxml.jackson.annotation.JsonView; import com.fasterxml.jackson.annotation.JsonView;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.codec.json.AbstractJackson2Codec; import org.springframework.http.codec.json.AbstractJackson2Codec;
import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
/** /**
* {@link ServerHttpMessageWriter} that resolves those annotation or request based Jackson 2 hints: * {@link ServerHttpMessageWriter} that resolves those annotation or request based Jackson 2 hints:
@ -44,9 +48,10 @@ public class Jackson2ServerHttpMessageWriter extends AbstractServerHttpMessageWr
} }
@Override @Override
protected Map<String, Object> resolveWriteHintsInternal(ResolvableType streamType, protected Map<String, Object> beforeWrite(ResolvableType streamType, ResolvableType elementType,
ResolvableType elementType, MediaType mediaType, ServerHttpRequest request) { MediaType mediaType, ServerHttpRequest request, ServerHttpResponse response) {
Map<String, Object> hints = new HashMap<>();
Object source = streamType.getSource(); Object source = streamType.getSource();
MethodParameter returnValue = (source instanceof MethodParameter ? (MethodParameter)source : null); MethodParameter returnValue = (source instanceof MethodParameter ? (MethodParameter)source : null);
if (returnValue != null) { if (returnValue != null) {
@ -57,10 +62,10 @@ public class Jackson2ServerHttpMessageWriter extends AbstractServerHttpMessageWr
throw new IllegalArgumentException( throw new IllegalArgumentException(
"@JsonView only supported for write hints with exactly 1 class argument: " + returnValue); "@JsonView only supported for write hints with exactly 1 class argument: " + returnValue);
} }
return Collections.singletonMap(AbstractJackson2Codec.JSON_VIEW_HINT, classes[0]); hints.put(AbstractJackson2Codec.JSON_VIEW_HINT, classes[0]);
} }
} }
return Collections.emptyMap(); return hints;
} }
} }

38
spring-web/src/main/java/org/springframework/http/codec/ServerHttpMessageReader.java

@ -18,14 +18,17 @@ package org.springframework.http.codec;
import java.util.Map; import java.util.Map;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
/** /**
* Server and annotation based controller specific {@link HttpMessageReader} that allows to * Server oriented {@link HttpMessageReader} that allows to resolve hints using annotations or
* resolve hints using annotations or request based information. * perform additional operation using {@link ServerHttpRequest} or {@link ServerHttpResponse}.
* *
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @since 5.0 * @since 5.0
@ -33,16 +36,37 @@ import org.springframework.http.server.reactive.ServerHttpRequest;
public interface ServerHttpMessageReader<T> extends HttpMessageReader<T> { public interface ServerHttpMessageReader<T> extends HttpMessageReader<T> {
/** /**
* Read a {@link Flux} of the given type form the given input message with additional server related
* parameters which could be used to create some hints or set the response status for example.
*
* Return hints that can be used to customize how the body should be read
* @param streamType the original type used in the method parameter. For annotation
* based controllers, the {@link MethodParameter} is available via {@link ResolvableType#getSource()}.
* @param elementType the stream element type to return
* Typically the value of a {@code Content-Type} header.
* @param request the current HTTP request
* @param response the current HTTP response
* @param hints additional information about how to read the body
* @return the converted {@link Flux} of elements
*/
Flux<T> read(ResolvableType streamType, ResolvableType elementType, ServerHttpRequest request,
ServerHttpResponse response, Map<String, Object> hints);
/**
* Read a {@link Mono} of the given type form the given input message with additional server related
* parameters which could be used to create some hints or set the response status for example.
*
* Return hints that can be used to customize how the body should be read * Return hints that can be used to customize how the body should be read
* @param streamType the original type used in the method parameter. For annotation * @param streamType the original type used in the method parameter. For annotation
* based controllers, the {@link MethodParameter} is available via {@link ResolvableType#getSource()}. * based controllers, the {@link MethodParameter} is available via {@link ResolvableType#getSource()}.
* @param elementType the stream element type to return * @param elementType the stream element type to return
* @param mediaType the media type to read, can be {@code null} if not specified.
* Typically the value of a {@code Content-Type} header. * Typically the value of a {@code Content-Type} header.
* @param request the current HTTP request * @param request the current HTTP request
* @return Additional information about how to read the body * @param response the current HTTP response
* @param hints additional information about how to read the body
* @return the converted {@link Mono} of object
*/ */
Map<String, Object> resolveReadHints(ResolvableType streamType, ResolvableType elementType, Mono<T> readMono(ResolvableType streamType, ResolvableType elementType, ServerHttpRequest request,
MediaType mediaType, ServerHttpRequest request); ServerHttpResponse response, Map<String, Object> hints);
} }

21
spring-web/src/main/java/org/springframework/http/codec/ServerHttpMessageWriter.java

@ -18,14 +18,18 @@ package org.springframework.http.codec;
import java.util.Map; import java.util.Map;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
/** /**
* Server oriented {@link HttpMessageWriter} that allows to resolve hints using annotations or * Server oriented {@link HttpMessageWriter} that allows to resolve hints using annotations or
* request based information. * perform additional operation using {@link ServerHttpRequest} or {@link ServerHttpResponse}.
* *
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @since 5.0 * @since 5.0
@ -33,16 +37,21 @@ import org.springframework.http.server.reactive.ServerHttpRequest;
public interface ServerHttpMessageWriter<T> extends HttpMessageWriter<T> { public interface ServerHttpMessageWriter<T> extends HttpMessageWriter<T> {
/** /**
* Return hints that can be used to customize how the body should be written * Write a given object to the given output message with additional server related
* parameters which could be used to create some hints or set the response status for example.
*
* @param streamType the original type used for the method return value. For annotation * @param streamType the original type used for the method return value. For annotation
* based controllers, the {@link MethodParameter} is available via {@link ResolvableType#getSource()}. * based controllers, the {@link MethodParameter} is available via {@link ResolvableType#getSource()}.
* Can be {@code null}.
* @param elementType the stream element type to process * @param elementType the stream element type to process
* @param mediaType the content type to use when writing. May be {@code null} to * @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. * indicate that the default content type of the converter must be used.
* @param request the current HTTP request * @param request the current HTTP request, can be {@code null}
* @return Additional information about how to write the body * @param response the current HTTP response, can be {@code null}
* @return a {@link Mono} that indicates completion or error
*/ */
Map<String, Object> resolveWriteHints(ResolvableType streamType, ResolvableType elementType, Mono<Void> write(Publisher<? extends T> inputStream, ResolvableType streamType,
MediaType mediaType, ServerHttpRequest request); ResolvableType elementType, MediaType mediaType, ServerHttpRequest request,
ServerHttpResponse response, Map<String, Object> hints);
} }

Loading…
Cancel
Save