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:
Sebastien Deleuze
2016-09-20 11:55:19 +02:00
parent 38f3d12e45
commit 857e77eec2
8 changed files with 143 additions and 85 deletions

View File

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

View File

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