Resolve target type for GenericHttpMessageConverter.canWrite/write
Issue: SPR-16877
This commit is contained in:
parent
cacd14c805
commit
b915e42c38
|
|
@ -28,6 +28,7 @@ import java.util.Set;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.springframework.core.GenericTypeResolver;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.ParameterizedTypeReference;
|
import org.springframework.core.ParameterizedTypeReference;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
|
|
@ -59,12 +60,13 @@ import org.springframework.web.servlet.HandlerMapping;
|
||||||
import org.springframework.web.util.UrlPathHelper;
|
import org.springframework.web.util.UrlPathHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extends {@link AbstractMessageConverterMethodArgumentResolver} with the ability to handle
|
* Extends {@link AbstractMessageConverterMethodArgumentResolver} with the ability to handle method
|
||||||
* method return values by writing to the response with {@link HttpMessageConverter HttpMessageConverters}.
|
* return values by writing to the response with {@link HttpMessageConverter HttpMessageConverters}.
|
||||||
*
|
*
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @author Brian Clozel
|
* @author Brian Clozel
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
|
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
|
||||||
|
|
@ -180,30 +182,30 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
|
||||||
|
|
||||||
Object body;
|
Object body;
|
||||||
Class<?> valueType;
|
Class<?> valueType;
|
||||||
Type declaredType;
|
Type targetType;
|
||||||
|
|
||||||
if (value instanceof CharSequence) {
|
if (value instanceof CharSequence) {
|
||||||
body = value.toString();
|
body = value.toString();
|
||||||
valueType = String.class;
|
valueType = String.class;
|
||||||
declaredType = String.class;
|
targetType = String.class;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
body = value;
|
body = value;
|
||||||
valueType = getReturnValueType(body, returnType);
|
valueType = getReturnValueType(body, returnType);
|
||||||
declaredType = getGenericType(returnType);
|
targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isResourceType(value, returnType)) {
|
if (isResourceType(value, returnType)) {
|
||||||
outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
|
outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
|
||||||
if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null
|
if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null &&
|
||||||
&& outputMessage.getServletResponse().getStatus() == 200) {
|
outputMessage.getServletResponse().getStatus() == 200) {
|
||||||
Resource resource = (Resource) value;
|
Resource resource = (Resource) value;
|
||||||
try {
|
try {
|
||||||
List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
|
List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
|
||||||
outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
|
outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
|
||||||
body = HttpRange.toResourceRegions(httpRanges, resource);
|
body = HttpRange.toResourceRegions(httpRanges, resource);
|
||||||
valueType = body.getClass();
|
valueType = body.getClass();
|
||||||
declaredType = RESOURCE_REGION_LIST_TYPE;
|
targetType = RESOURCE_REGION_LIST_TYPE;
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException ex) {
|
catch (IllegalArgumentException ex) {
|
||||||
outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
|
outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
|
||||||
|
|
@ -225,7 +227,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
|
||||||
else {
|
else {
|
||||||
HttpServletRequest request = inputMessage.getServletRequest();
|
HttpServletRequest request = inputMessage.getServletRequest();
|
||||||
List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);
|
List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);
|
||||||
List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType);
|
List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, targetType);
|
||||||
|
|
||||||
if (body != null && producibleMediaTypes.isEmpty()) {
|
if (body != null && producibleMediaTypes.isEmpty()) {
|
||||||
throw new HttpMessageNotWritableException(
|
throw new HttpMessageNotWritableException(
|
||||||
|
|
@ -270,22 +272,22 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
|
||||||
if (selectedMediaType != null) {
|
if (selectedMediaType != null) {
|
||||||
selectedMediaType = selectedMediaType.removeQualityValue();
|
selectedMediaType = selectedMediaType.removeQualityValue();
|
||||||
for (HttpMessageConverter<?> converter : this.messageConverters) {
|
for (HttpMessageConverter<?> converter : this.messageConverters) {
|
||||||
GenericHttpMessageConverter genericConverter =
|
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
|
||||||
(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
|
(GenericHttpMessageConverter<?>) converter : null);
|
||||||
if (genericConverter != null ?
|
if (genericConverter != null ?
|
||||||
((GenericHttpMessageConverter) converter).canWrite(declaredType, valueType, selectedMediaType) :
|
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
|
||||||
converter.canWrite(valueType, selectedMediaType)) {
|
converter.canWrite(valueType, selectedMediaType)) {
|
||||||
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
|
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
|
||||||
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
|
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
|
||||||
inputMessage, outputMessage);
|
inputMessage, outputMessage);
|
||||||
if (body != null) {
|
if (body != null) {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
Object formatted = body instanceof CharSequence ? "\"" + body + "\"" : body;
|
Object formatted = (body instanceof CharSequence ? "\"" + body + "\"" : body);
|
||||||
logger.debug("Writing [" + formatted + "]");
|
logger.debug("Writing [" + formatted + "]");
|
||||||
}
|
}
|
||||||
addContentDispositionHeader(inputMessage, outputMessage);
|
addContentDispositionHeader(inputMessage, outputMessage);
|
||||||
if (genericConverter != null) {
|
if (genericConverter != null) {
|
||||||
genericConverter.write(body, declaredType, selectedMediaType, outputMessage);
|
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
|
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
|
||||||
|
|
@ -356,18 +358,19 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
|
||||||
* @since 4.2
|
* @since 4.2
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected List<MediaType> getProducibleMediaTypes(HttpServletRequest request, Class<?> valueClass,
|
protected List<MediaType> getProducibleMediaTypes(
|
||||||
@Nullable Type declaredType) {
|
HttpServletRequest request, Class<?> valueClass, @Nullable Type targetType) {
|
||||||
|
|
||||||
Set<MediaType> mediaTypes = (Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
|
Set<MediaType> mediaTypes =
|
||||||
|
(Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
|
||||||
if (!CollectionUtils.isEmpty(mediaTypes)) {
|
if (!CollectionUtils.isEmpty(mediaTypes)) {
|
||||||
return new ArrayList<>(mediaTypes);
|
return new ArrayList<>(mediaTypes);
|
||||||
}
|
}
|
||||||
else if (!this.allSupportedMediaTypes.isEmpty()) {
|
else if (!this.allSupportedMediaTypes.isEmpty()) {
|
||||||
List<MediaType> result = new ArrayList<>();
|
List<MediaType> result = new ArrayList<>();
|
||||||
for (HttpMessageConverter<?> converter : this.messageConverters) {
|
for (HttpMessageConverter<?> converter : this.messageConverters) {
|
||||||
if (converter instanceof GenericHttpMessageConverter && declaredType != null) {
|
if (converter instanceof GenericHttpMessageConverter && targetType != null) {
|
||||||
if (((GenericHttpMessageConverter<?>) converter).canWrite(declaredType, valueClass, null)) {
|
if (((GenericHttpMessageConverter<?>) converter).canWrite(targetType, valueClass, null)) {
|
||||||
result.addAll(converter.getSupportedMediaTypes());
|
result.addAll(converter.getSupportedMediaTypes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2017 the original author or authors.
|
* Copyright 2002-2018 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -932,7 +932,20 @@ public class RequestResponseBodyMethodProcessorTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class JacksonController {
|
private static class BaseController<T> {
|
||||||
|
|
||||||
|
@RequestMapping
|
||||||
|
@ResponseBody
|
||||||
|
public List<T> handleTypeInfoList() {
|
||||||
|
List<T> list = new ArrayList<>();
|
||||||
|
list.add((T) new Foo("foo"));
|
||||||
|
list.add((T) new Bar("bar"));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class JacksonController extends BaseController<ParentClass> {
|
||||||
|
|
||||||
@RequestMapping
|
@RequestMapping
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
|
|
@ -969,15 +982,6 @@ public class RequestResponseBodyMethodProcessorTests {
|
||||||
return entity.getBody();
|
return entity.getBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping
|
|
||||||
@ResponseBody
|
|
||||||
public List<ParentClass> handleTypeInfoList() {
|
|
||||||
List<ParentClass> list = new ArrayList<>();
|
|
||||||
list.add(new Foo("foo"));
|
|
||||||
list.add(new Bar("bar"));
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequestMapping
|
@RequestMapping
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Identifiable handleSubType() {
|
public Identifiable handleSubType() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue