Respect produces condition in @ResponseBody algorithm

This commit is contained in:
Rossen Stoyanchev
2016-05-26 13:34:08 -04:00
parent a017e2848a
commit 2263954ad7
2 changed files with 38 additions and 18 deletions

View File

@@ -35,15 +35,15 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.convert.ConversionService;
import org.springframework.http.MediaType;
import org.springframework.http.converter.reactive.HttpMessageConverter;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.reactive.HandlerResult;
import org.springframework.web.reactive.HandlerResultHandler;
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
import org.springframework.web.reactive.accept.HeaderContentTypeResolver;
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
import org.springframework.web.server.NotAcceptableStatusException;
import org.springframework.web.server.ServerWebExchange;
@@ -171,11 +171,10 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered
elementType = returnType;
}
ServerHttpRequest request = exchange.getRequest();
List<MediaType> compatibleMediaTypes = getCompatibleMediaTypes(exchange, elementType);
if (compatibleMediaTypes.isEmpty()) {
if (result.getReturnValue().isPresent()) {
List<MediaType> mediaTypes = getProducibleMediaTypes(elementType);
List<MediaType> mediaTypes = getProducibleMediaTypes(exchange, elementType);
return Mono.error(new NotAcceptableStatusException(mediaTypes));
}
return Mono.empty();
@@ -197,13 +196,13 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered
ResolvableType elementType) {
List<MediaType> acceptableMediaTypes = getAcceptableMediaTypes(exchange);
List<MediaType> producibleMediaTypes = getProducibleMediaTypes(elementType);
List<MediaType> producibleMediaTypes = getProducibleMediaTypes(exchange, elementType);
Set<MediaType> compatibleMediaTypes = new LinkedHashSet<>();
for (MediaType acceptable : acceptableMediaTypes) {
for (MediaType producible : producibleMediaTypes) {
if (acceptable.isCompatibleWith(producible)) {
compatibleMediaTypes.add(getMostSpecificMediaType(acceptable, producible));
compatibleMediaTypes.add(selectMoreSpecificMediaType(acceptable, producible));
}
}
}
@@ -218,19 +217,21 @@ public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered
return (mediaTypes.isEmpty() ? Collections.singletonList(MediaType.ALL) : mediaTypes);
}
private List<MediaType> getProducibleMediaTypes(ResolvableType type) {
return this.messageConverters.stream()
.filter(converter -> converter.canWrite(type, null))
.flatMap(converter -> converter.getWritableMediaTypes().stream())
.collect(Collectors.collectingAndThen(Collectors.toList(), result -> {
if (result.isEmpty()) {
result.add(MediaType.ALL);
}
return result;
}));
private List<MediaType> getProducibleMediaTypes(ServerWebExchange exchange, ResolvableType type) {
Optional<?> optional = exchange.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
if (optional.isPresent()) {
Set<MediaType> mediaTypes = (Set<MediaType>) optional.get();
return new ArrayList<>(mediaTypes);
}
else {
return this.messageConverters.stream()
.filter(converter -> converter.canWrite(type, null))
.flatMap(converter -> converter.getWritableMediaTypes().stream())
.collect(Collectors.toList());
}
}
private MediaType getMostSpecificMediaType(MediaType acceptable, MediaType producible) {
private MediaType selectMoreSpecificMediaType(MediaType acceptable, MediaType producible) {
producible = producible.copyQualityValue(acceptable);
Comparator<MediaType> comparator = MediaType.SPECIFICITY_COMPARATOR;
return (comparator.compare(acceptable, producible) <= 0 ? acceptable : producible);

View File

@@ -19,9 +19,13 @@ package org.springframework.web.reactive.result.method.annotation;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.print.attribute.standard.Media;
import org.junit.Test;
import org.reactivestreams.Publisher;
@@ -40,6 +44,7 @@ import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.reactive.HandlerResult;
import org.springframework.web.reactive.HandlerResultHandler;
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
@@ -88,7 +93,7 @@ public class ResponseBodyResultHandlerTests {
}
@Test
public void contentTypeResolver() throws Exception {
public void usesContentTypeResolver() throws Exception {
MediaType contentType = MediaType.APPLICATION_JSON_UTF8;
RequestedContentTypeResolver resolver = new FixedContentTypeResolver(contentType);
HandlerResultHandler handler = createHandler(resolver, new StringEncoder(), new JacksonJsonEncoder());
@@ -100,6 +105,20 @@ public class ResponseBodyResultHandlerTests {
assertEquals(contentType, exchange.getResponse().getHeaders().getContentType());
}
@Test
public void detectsProducibleMediaTypesAttribute() throws Exception {
ServerWebExchange exchange = createExchange("/foo");
Set<MediaType> mediaTypes = Collections.singleton(MediaType.APPLICATION_JSON);
exchange.getAttributes().put(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
HandlerResultHandler handler = createHandler(new StringEncoder(), new JacksonJsonEncoder());
HandlerResult result = new HandlerResult(new Object(), "fooValue", ResolvableType.forClass(String.class));
handler.handleResult(exchange, result).get();
assertEquals(MediaType.APPLICATION_JSON, exchange.getResponse().getHeaders().getContentType());
}
private ResponseBodyResultHandler createHandler(Encoder<?>... encoders) {
return createHandler(new HeaderContentTypeResolver(), encoders);