Use ResponseEntity Content-Type as producible media type
Issue: SPR-16172
This commit is contained in:
@@ -152,6 +152,10 @@ public abstract class HandlerResultHandlerSupport implements Ordered {
|
||||
private List<MediaType> getProducibleTypes(ServerWebExchange exchange,
|
||||
Supplier<List<MediaType>> producibleTypesSupplier) {
|
||||
|
||||
MediaType contentType = exchange.getResponse().getHeaders().getContentType();
|
||||
if (contentType != null && contentType.isConcrete()) {
|
||||
return Collections.singletonList(contentType);
|
||||
}
|
||||
Set<MediaType> mediaTypes = exchange.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
|
||||
return (mediaTypes != null ? new ArrayList<>(mediaTypes) : producibleTypesSupplier.get());
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ public abstract class AbstractMessageWriterResultHandler extends HandlerResultHa
|
||||
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
MediaType bestMediaType = selectMediaType(exchange, () -> getProducibleMediaTypes(elementType));
|
||||
MediaType bestMediaType = selectMediaType(exchange, () -> getMediaTypesFor(elementType));
|
||||
if (bestMediaType != null) {
|
||||
for (HttpMessageWriter<?> writer : getMessageWriters()) {
|
||||
if (writer.canWrite(elementType, bestMediaType)) {
|
||||
@@ -121,12 +121,12 @@ public abstract class AbstractMessageWriterResultHandler extends HandlerResultHa
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (getProducibleMediaTypes(elementType).isEmpty()) {
|
||||
return Mono.error(new IllegalStateException("No converter for return value type: " + elementType));
|
||||
if (getMediaTypesFor(elementType).isEmpty()) {
|
||||
return Mono.error(new IllegalStateException("No writer for : " + elementType));
|
||||
}
|
||||
}
|
||||
|
||||
return Mono.error(new NotAcceptableStatusException(getProducibleMediaTypes(elementType)));
|
||||
return Mono.error(new NotAcceptableStatusException(getMediaTypesFor(elementType)));
|
||||
}
|
||||
|
||||
private ResolvableType getElementType(ReactiveAdapter adapter, ResolvableType genericType) {
|
||||
@@ -141,7 +141,7 @@ public abstract class AbstractMessageWriterResultHandler extends HandlerResultHa
|
||||
}
|
||||
}
|
||||
|
||||
private List<MediaType> getProducibleMediaTypes(ResolvableType elementType) {
|
||||
private List<MediaType> getMediaTypesFor(ResolvableType elementType) {
|
||||
return getMessageWriters().stream()
|
||||
.filter(converter -> converter.canWrite(elementType, null))
|
||||
.flatMap(converter -> converter.getWritableMediaTypes().stream())
|
||||
|
||||
@@ -155,6 +155,16 @@ public class RequestMappingMessageConversionIntegrationTests extends AbstractReq
|
||||
assertEquals(expected, performGet("/person-response/mono-response-entity", JSON, Person.class).getBody());
|
||||
}
|
||||
|
||||
@Test // SPR-16172
|
||||
public void personResponseBodyWithMonoResponseEntityXml() throws Exception {
|
||||
|
||||
String actual = performGet("/person-response/mono-response-entity-xml",
|
||||
new HttpHeaders(), String.class).getBody();
|
||||
|
||||
assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
|
||||
"<person><name>Robert</name></person>", actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void personResponseBodyWithList() throws Exception {
|
||||
List<?> expected = asList(new Person("Robert"), new Person("Marie"));
|
||||
@@ -465,6 +475,12 @@ public class RequestMappingMessageConversionIntegrationTests extends AbstractReq
|
||||
return ResponseEntity.ok(body);
|
||||
}
|
||||
|
||||
@GetMapping("/mono-response-entity-xml")
|
||||
public ResponseEntity<Mono<Person>> getMonoResponseEntityXml() {
|
||||
Mono<Person> body = Mono.just(new Person("Robert"));
|
||||
return ResponseEntity.ok().contentType(MediaType.APPLICATION_XML).body(body);
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
public List<Person> getList() {
|
||||
return asList(new Person("Robert"), new Person("Marie"));
|
||||
|
||||
@@ -212,7 +212,10 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
|
||||
|
||||
HttpServletRequest request = inputMessage.getServletRequest();
|
||||
List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);
|
||||
List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType);
|
||||
|
||||
MediaType contentType = outputMessage.getHeaders().getContentType();
|
||||
List<MediaType> producibleMediaTypes = (contentType != null && contentType.isConcrete() ?
|
||||
Collections.singletonList(contentType) : getProducibleMediaTypes(request, valueType, declaredType));
|
||||
|
||||
if (outputValue != null && producibleMediaTypes.isEmpty()) {
|
||||
throw new HttpMessageNotWritableException("No converter found for return value of type: " + valueType);
|
||||
|
||||
@@ -83,6 +83,8 @@ import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.http.converter.HttpMessageNotWritableException;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.MarshallingHttpMessageConverter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.mock.web.test.MockHttpServletRequest;
|
||||
@@ -934,6 +936,26 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||
assertEquals(404, response.getStatus());
|
||||
}
|
||||
|
||||
@Test // SPR-16172
|
||||
public void httpEntityWithContentType() throws Exception {
|
||||
initServlet(wac -> {
|
||||
RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
|
||||
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
|
||||
messageConverters.add(new MappingJackson2HttpMessageConverter());
|
||||
messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
|
||||
adapterDef.getPropertyValues().add("messageConverters", messageConverters);
|
||||
wac.registerBeanDefinition("handlerAdapter", adapterDef);
|
||||
}, ResponseEntityController.class);
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test-entity");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
getServlet().service(request, response);
|
||||
assertEquals(200, response.getStatus());
|
||||
assertEquals("application/xml", response.getHeader("Content-Type"));
|
||||
assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
|
||||
"<testEntity><name>Foo Bar</name></testEntity>", response.getContentAsString());
|
||||
}
|
||||
|
||||
@Test // SPR-6877
|
||||
public void overlappingMessageConvertersRequestBody() throws Exception {
|
||||
initServlet(wac -> {
|
||||
@@ -3210,7 +3232,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||
@Controller
|
||||
public static class ResponseEntityController {
|
||||
|
||||
@RequestMapping(path = "/foo", method = RequestMethod.POST)
|
||||
@PostMapping("/foo")
|
||||
public ResponseEntity<String> foo(HttpEntity<byte[]> requestEntity) throws Exception {
|
||||
assertNotNull(requestEntity);
|
||||
assertEquals("MyValue", requestEntity.getHeaders().getFirst("MyRequestHeader"));
|
||||
@@ -3222,12 +3244,12 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||
return ResponseEntity.created(location).header("MyResponseHeader", "MyValue").body(body);
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/bar", method = RequestMethod.GET)
|
||||
@GetMapping("/bar")
|
||||
public ResponseEntity<Void> bar() {
|
||||
return ResponseEntity.notFound().header("MyResponseHeader", "MyValue").build();
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/baz", method = RequestMethod.GET)
|
||||
@GetMapping("/baz")
|
||||
public ResponseEntity<String> baz() {
|
||||
return ResponseEntity.ok().header("MyResponseHeader", "MyValue").body("body");
|
||||
}
|
||||
@@ -3237,10 +3259,32 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||
return ResponseEntity.ok().header("h1", "v1").build();
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/stores", method = RequestMethod.GET)
|
||||
@GetMapping("/stores")
|
||||
public ResponseEntity<String> getResource() {
|
||||
return ResponseEntity.ok().body("body");
|
||||
}
|
||||
|
||||
@GetMapping("/test-entity")
|
||||
public ResponseEntity<TestEntity> testEntity() {
|
||||
TestEntity entity = new TestEntity();
|
||||
entity.setName("Foo Bar");
|
||||
return ResponseEntity.ok().contentType(MediaType.APPLICATION_XML).body(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlRootElement
|
||||
static class TestEntity {
|
||||
|
||||
private String name;
|
||||
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@Controller
|
||||
|
||||
Reference in New Issue
Block a user