From 2263954ad7d760c145eeaf4b02c2e9a68f118ad1 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 26 May 2016 13:34:08 -0400 Subject: [PATCH] Respect produces condition in @ResponseBody algorithm --- .../annotation/ResponseBodyResultHandler.java | 35 ++++++++++--------- .../ResponseBodyResultHandlerTests.java | 21 ++++++++++- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandler.java index 9299bdb0f11..c5d71f19fe4 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandler.java @@ -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 compatibleMediaTypes = getCompatibleMediaTypes(exchange, elementType); if (compatibleMediaTypes.isEmpty()) { if (result.getReturnValue().isPresent()) { - List mediaTypes = getProducibleMediaTypes(elementType); + List 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 acceptableMediaTypes = getAcceptableMediaTypes(exchange); - List producibleMediaTypes = getProducibleMediaTypes(elementType); + List producibleMediaTypes = getProducibleMediaTypes(exchange, elementType); Set 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 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 getProducibleMediaTypes(ServerWebExchange exchange, ResolvableType type) { + Optional optional = exchange.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); + if (optional.isPresent()) { + Set mediaTypes = (Set) 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 comparator = MediaType.SPECIFICITY_COMPARATOR; return (comparator.compare(acceptable, producible) <= 0 ? acceptable : producible); diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandlerTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandlerTests.java index 208bc45ed5e..15043cb1f87 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandlerTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseBodyResultHandlerTests.java @@ -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 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);