Skip to content

Commit b915e42

Browse files
committed
Resolve target type for GenericHttpMessageConverter.canWrite/write
Issue: SPR-16877
1 parent cacd14c commit b915e42

File tree

2 files changed

+37
-30
lines changed

2 files changed

+37
-30
lines changed

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import javax.servlet.http.HttpServletRequest;
2929
import javax.servlet.http.HttpServletResponse;
3030

31+
import org.springframework.core.GenericTypeResolver;
3132
import org.springframework.core.MethodParameter;
3233
import org.springframework.core.ParameterizedTypeReference;
3334
import org.springframework.core.ResolvableType;
@@ -59,12 +60,13 @@
5960
import org.springframework.web.util.UrlPathHelper;
6061

6162
/**
62-
* Extends {@link AbstractMessageConverterMethodArgumentResolver} with the ability to handle
63-
* method return values by writing to the response with {@link HttpMessageConverter HttpMessageConverters}.
63+
* Extends {@link AbstractMessageConverterMethodArgumentResolver} with the ability to handle method
64+
* return values by writing to the response with {@link HttpMessageConverter HttpMessageConverters}.
6465
*
6566
* @author Arjen Poutsma
6667
* @author Rossen Stoyanchev
6768
* @author Brian Clozel
69+
* @author Juergen Hoeller
6870
* @since 3.1
6971
*/
7072
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
@@ -180,30 +182,30 @@ protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter
180182

181183
Object body;
182184
Class<?> valueType;
183-
Type declaredType;
185+
Type targetType;
184186

185187
if (value instanceof CharSequence) {
186188
body = value.toString();
187189
valueType = String.class;
188-
declaredType = String.class;
190+
targetType = String.class;
189191
}
190192
else {
191193
body = value;
192194
valueType = getReturnValueType(body, returnType);
193-
declaredType = getGenericType(returnType);
195+
targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
194196
}
195197

196198
if (isResourceType(value, returnType)) {
197199
outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
198-
if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null
199-
&& outputMessage.getServletResponse().getStatus() == 200) {
200+
if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null &&
201+
outputMessage.getServletResponse().getStatus() == 200) {
200202
Resource resource = (Resource) value;
201203
try {
202204
List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
203205
outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
204206
body = HttpRange.toResourceRegions(httpRanges, resource);
205207
valueType = body.getClass();
206-
declaredType = RESOURCE_REGION_LIST_TYPE;
208+
targetType = RESOURCE_REGION_LIST_TYPE;
207209
}
208210
catch (IllegalArgumentException ex) {
209211
outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
@@ -225,7 +227,7 @@ protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter
225227
else {
226228
HttpServletRequest request = inputMessage.getServletRequest();
227229
List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);
228-
List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType);
230+
List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, targetType);
229231

230232
if (body != null && producibleMediaTypes.isEmpty()) {
231233
throw new HttpMessageNotWritableException(
@@ -270,22 +272,22 @@ else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICAT
270272
if (selectedMediaType != null) {
271273
selectedMediaType = selectedMediaType.removeQualityValue();
272274
for (HttpMessageConverter<?> converter : this.messageConverters) {
273-
GenericHttpMessageConverter genericConverter =
274-
(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
275+
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
276+
(GenericHttpMessageConverter<?>) converter : null);
275277
if (genericConverter != null ?
276-
((GenericHttpMessageConverter) converter).canWrite(declaredType, valueType, selectedMediaType) :
278+
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
277279
converter.canWrite(valueType, selectedMediaType)) {
278280
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
279281
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
280282
inputMessage, outputMessage);
281283
if (body != null) {
282284
if (logger.isDebugEnabled()) {
283-
Object formatted = body instanceof CharSequence ? "\"" + body + "\"" : body;
285+
Object formatted = (body instanceof CharSequence ? "\"" + body + "\"" : body);
284286
logger.debug("Writing [" + formatted + "]");
285287
}
286288
addContentDispositionHeader(inputMessage, outputMessage);
287289
if (genericConverter != null) {
288-
genericConverter.write(body, declaredType, selectedMediaType, outputMessage);
290+
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
289291
}
290292
else {
291293
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
@@ -356,18 +358,19 @@ protected List<MediaType> getProducibleMediaTypes(HttpServletRequest request, Cl
356358
* @since 4.2
357359
*/
358360
@SuppressWarnings("unchecked")
359-
protected List<MediaType> getProducibleMediaTypes(HttpServletRequest request, Class<?> valueClass,
360-
@Nullable Type declaredType) {
361+
protected List<MediaType> getProducibleMediaTypes(
362+
HttpServletRequest request, Class<?> valueClass, @Nullable Type targetType) {
361363

362-
Set<MediaType> mediaTypes = (Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
364+
Set<MediaType> mediaTypes =
365+
(Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
363366
if (!CollectionUtils.isEmpty(mediaTypes)) {
364367
return new ArrayList<>(mediaTypes);
365368
}
366369
else if (!this.allSupportedMediaTypes.isEmpty()) {
367370
List<MediaType> result = new ArrayList<>();
368371
for (HttpMessageConverter<?> converter : this.messageConverters) {
369-
if (converter instanceof GenericHttpMessageConverter && declaredType != null) {
370-
if (((GenericHttpMessageConverter<?>) converter).canWrite(declaredType, valueClass, null)) {
372+
if (converter instanceof GenericHttpMessageConverter && targetType != null) {
373+
if (((GenericHttpMessageConverter<?>) converter).canWrite(targetType, valueClass, null)) {
371374
result.addAll(converter.getSupportedMediaTypes());
372375
}
373376
}

spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorTests.java

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -932,7 +932,20 @@ public Bar(String parentProperty) {
932932
}
933933

934934

935-
private static class JacksonController {
935+
private static class BaseController<T> {
936+
937+
@RequestMapping
938+
@ResponseBody
939+
public List<T> handleTypeInfoList() {
940+
List<T> list = new ArrayList<>();
941+
list.add((T) new Foo("foo"));
942+
list.add((T) new Bar("bar"));
943+
return list;
944+
}
945+
}
946+
947+
948+
private static class JacksonController extends BaseController<ParentClass> {
936949

937950
@RequestMapping
938951
@ResponseBody
@@ -969,15 +982,6 @@ public JacksonViewBean handleHttpEntity(@JsonView(MyJacksonView1.class) HttpEnti
969982
return entity.getBody();
970983
}
971984

972-
@RequestMapping
973-
@ResponseBody
974-
public List<ParentClass> handleTypeInfoList() {
975-
List<ParentClass> list = new ArrayList<>();
976-
list.add(new Foo("foo"));
977-
list.add(new Bar("bar"));
978-
return list;
979-
}
980-
981985
@RequestMapping
982986
@ResponseBody
983987
public Identifiable handleSubType() {

0 commit comments

Comments
 (0)