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
This commit is contained in:
@@ -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)));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
* 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 Flux} of elements
|
||||||
*/
|
*/
|
||||||
Map<String, Object> resolveReadHints(ResolvableType streamType, ResolvableType elementType,
|
Flux<T> read(ResolvableType streamType, ResolvableType elementType, ServerHttpRequest request,
|
||||||
MediaType mediaType, 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
|
||||||
|
* @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 Mono} of object
|
||||||
|
*/
|
||||||
|
Mono<T> readMono(ResolvableType streamType, ResolvableType elementType, ServerHttpRequest request,
|
||||||
|
ServerHttpResponse response, Map<String, Object> hints);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user