@ -1,5 +1,5 @@
@@ -1,5 +1,5 @@
/ *
* Copyright 2002 - 2015 the original author or authors .
* Copyright 2002 - 2016 the original author or authors .
*
* Licensed under the Apache License , Version 2 . 0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
@ -30,6 +30,7 @@ import javax.servlet.http.HttpServletRequest;
@@ -30,6 +30,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse ;
import org.springframework.core.MethodParameter ;
import org.springframework.core.ResolvableType ;
import org.springframework.http.HttpEntity ;
import org.springframework.http.HttpHeaders ;
import org.springframework.http.HttpOutputMessage ;
@ -62,17 +63,6 @@ import org.springframework.web.util.UrlPathHelper;
@@ -62,17 +63,6 @@ import org.springframework.web.util.UrlPathHelper;
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
implements HandlerMethodReturnValueHandler {
private static final MediaType MEDIA_TYPE_APPLICATION = new MediaType ( "application" ) ;
private static final UrlPathHelper RAW_URL_PATH_HELPER = new UrlPathHelper ( ) ;
private static final UrlPathHelper DECODING_URL_PATH_HELPER = new UrlPathHelper ( ) ;
static {
RAW_URL_PATH_HELPER . setRemoveSemicolonContent ( false ) ;
RAW_URL_PATH_HELPER . setUrlDecode ( false ) ;
}
/* Extensions associated with the built-in message converters */
private static final Set < String > WHITELISTED_EXTENSIONS = new HashSet < String > ( Arrays . asList (
"txt" , "text" , "yml" , "properties" , "csv" ,
@ -82,6 +72,17 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
@@ -82,6 +72,17 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
private static final Set < String > WHITELISTED_MEDIA_BASE_TYPES = new HashSet < String > (
Arrays . asList ( "audio" , "image" , "video" ) ) ;
private static final MediaType MEDIA_TYPE_APPLICATION = new MediaType ( "application" ) ;
private static final UrlPathHelper DECODING_URL_PATH_HELPER = new UrlPathHelper ( ) ;
private static final UrlPathHelper RAW_URL_PATH_HELPER = new UrlPathHelper ( ) ;
static {
RAW_URL_PATH_HELPER . setRemoveSemicolonContent ( false ) ;
RAW_URL_PATH_HELPER . setUrlDecode ( false ) ;
}
private final ContentNegotiationManager contentNegotiationManager ;
@ -145,17 +146,17 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
@@ -145,17 +146,17 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
* Writes the given return value to the given web request . Delegates to
* { @link # writeWithMessageConverters ( Object , MethodParameter , ServletServerHttpRequest , ServletServerHttpResponse ) }
* /
protected < T > void writeWithMessageConverters ( T returnV alue, MethodParameter returnType , NativeWebRequest webRequest )
protected < T > void writeWithMessageConverters ( T v alue, MethodParameter returnType , NativeWebRequest webRequest )
throws IOException , HttpMediaTypeNotAcceptableException , HttpMessageNotWritableException {
ServletServerHttpRequest inputMessage = createInputMessage ( webRequest ) ;
ServletServerHttpResponse outputMessage = createOutputMessage ( webRequest ) ;
writeWithMessageConverters ( returnV alue, returnType , inputMessage , outputMessage ) ;
writeWithMessageConverters ( v alue, returnType , inputMessage , outputMessage ) ;
}
/ * *
* Writes the given return type to the given output message .
* @param returnV alue the value to write to the output message
* @param v alue the value to write to the output message
* @param returnType the type of the value
* @param inputMessage the input messages . Used to inspect the { @code Accept } header .
* @param outputMessage the output message to write to
@ -164,18 +165,18 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
@@ -164,18 +165,18 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
* the request cannot be met by the message converters
* /
@SuppressWarnings ( "unchecked" )
protected < T > void writeWithMessageConverters ( T returnV alue, MethodParameter returnType ,
protected < T > void writeWithMessageConverters ( T v alue, MethodParameter returnType ,
ServletServerHttpRequest inputMessage , ServletServerHttpResponse outputMessage )
throws IOException , HttpMediaTypeNotAcceptableException , HttpMessageNotWritableException {
Class < ? > returnValueClass = getReturnValueType ( returnV alue, returnType ) ;
Type returnValue Type = getGenericType ( returnType ) ;
HttpServletRequest se rvletR equest = inputMessage . getServletRequest ( ) ;
List < MediaType > requestedMediaTypes = getAcceptableMediaTypes ( se rvletR equest) ;
List < MediaType > producibleMediaTypes = getProducibleMediaTypes ( se rvletR equest, returnValueClass , returnValue Type) ;
Class < ? > valueType = getReturnValueType ( v alue, returnType ) ;
Type declared Type = getGenericType ( returnType ) ;
HttpServletRequest request = inputMessage . getServletRequest ( ) ;
List < MediaType > requestedMediaTypes = getAcceptableMediaTypes ( request ) ;
List < MediaType > producibleMediaTypes = getProducibleMediaTypes ( request , valueType , declared Type) ;
if ( returnV alue ! = null & & producibleMediaTypes . isEmpty ( ) ) {
throw new IllegalArgumentException ( "No converter found for return value of type: " + returnValueClass ) ;
if ( v alue ! = null & & producibleMediaTypes . isEmpty ( ) ) {
throw new IllegalArgumentException ( "No converter found for return value of type: " + valueType ) ;
}
Set < MediaType > compatibleMediaTypes = new LinkedHashSet < MediaType > ( ) ;
@ -187,7 +188,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
@@ -187,7 +188,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
}
}
if ( compatibleMediaTypes . isEmpty ( ) ) {
if ( returnV alue ! = null ) {
if ( v alue ! = null ) {
throw new HttpMediaTypeNotAcceptableException ( producibleMediaTypes ) ;
}
return ;
@ -212,34 +213,33 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
@@ -212,34 +213,33 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
selectedMediaType = selectedMediaType . removeQualityValue ( ) ;
for ( HttpMessageConverter < ? > messageConverter : this . messageConverters ) {
if ( messageConverter instanceof GenericHttpMessageConverter ) {
if ( ( ( GenericHttpMessageConverter < T > ) messageConverter ) . canWrite ( returnValueType ,
returnValueClass , selectedMediaType ) ) {
returnV alue = ( T ) getAdvice ( ) . beforeBodyWrite ( returnV alue, returnType , selectedMediaType ,
if ( ( ( GenericHttpMessageConverter < T > ) messageConverter ) . canWrite (
declaredType , valueType , selectedMediaType ) ) {
v alue = ( T ) getAdvice ( ) . beforeBodyWrite ( v alue, returnType , selectedMediaType ,
( Class < ? extends HttpMessageConverter < ? > > ) messageConverter . getClass ( ) ,
inputMessage , outputMessage ) ;
if ( returnV alue ! = null ) {
if ( v alue ! = null ) {
addContentDispositionHeader ( inputMessage , outputMessage ) ;
( ( GenericHttpMessageConverter < T > ) messageConverter ) . write ( returnValue ,
returnValue Type, selectedMediaType , outputMessage ) ;
( ( GenericHttpMessageConverter < T > ) messageConverter ) . write (
value , declared Type, selectedMediaType , outputMessage ) ;
if ( logger . isDebugEnabled ( ) ) {
logger . debug ( "Written [" + returnV alue + "] as \"" +
selectedMediaType + "\" using [" + messageConverter + "]" ) ;
logger . debug ( "Written [" + v alue + "] as \"" + selectedMediaType +
"\" using [" + messageConverter + "]" ) ;
}
}
return ;
}
}
else if ( messageConverter . canWrite ( returnValueClass , selectedMediaType ) ) {
returnV alue = ( T ) getAdvice ( ) . beforeBodyWrite ( returnV alue, returnType , selectedMediaType ,
else if ( messageConverter . canWrite ( valueType , selectedMediaType ) ) {
v alue = ( T ) getAdvice ( ) . beforeBodyWrite ( v alue, returnType , selectedMediaType ,
( Class < ? extends HttpMessageConverter < ? > > ) messageConverter . getClass ( ) ,
inputMessage , outputMessage ) ;
if ( returnV alue ! = null ) {
if ( v alue ! = null ) {
addContentDispositionHeader ( inputMessage , outputMessage ) ;
( ( HttpMessageConverter < T > ) messageConverter ) . write ( returnValue ,
selectedMediaType , outputMessage ) ;
( ( HttpMessageConverter < T > ) messageConverter ) . write ( value , selectedMediaType , outputMessage ) ;
if ( logger . isDebugEnabled ( ) ) {
logger . debug ( "Written [" + returnV alue + "] as \"" +
selectedMediaType + "\" using [" + messageConverter + "]" ) ;
logger . debug ( "Written [" + v alue + "] as \"" + selectedMediaType +
"\" using [" + messageConverter + "]" ) ;
}
}
return ;
@ -247,43 +247,40 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
@@ -247,43 +247,40 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
}
}
if ( returnV alue ! = null ) {
if ( v alue ! = null ) {
throw new HttpMediaTypeNotAcceptableException ( this . allSupportedMediaTypes ) ;
}
}
/ * *
* Return the type of the value to be written to the response . Typically this
* is a simple check via getClass on the returnV alue but if the returnValue is
* null , then the returnT ype needs to be examined possibly including generic
* type determination ( e . g . { @code ResponseEntity < T > } ) .
* Return the type of the value to be written to the response . Typically this is
* a simple check via getClass on the v alue but if the value is null , then the
* return t ype needs to be examined possibly including generic type determination
* ( e . g . { @code ResponseEntity < T > } ) .
* /
protected Class < ? > getReturnValueType ( Object returnV alue, MethodParameter returnType ) {
return ( returnV alue ! = null ? returnV alue. getClass ( ) : returnType . getParameterType ( ) ) ;
protected Class < ? > getReturnValueType ( Object v alue, MethodParameter returnType ) {
return ( v alue ! = null ? v alue. getClass ( ) : returnType . getParameterType ( ) ) ;
}
/ * *
* Return the generic type of the { @code returnType } ( or of the nested type if it is
* a { @link HttpEntity } ) .
* Return the generic type of the { @code returnType } ( or of the nested type
* if it is an { @link HttpEntity } ) .
* /
private Type getGenericType ( MethodParameter returnType ) {
Type type ;
if ( HttpEntity . class . isAssignableFrom ( returnType . getParameterType ( ) ) ) {
returnType . increaseNestingLevel ( ) ;
type = returnType . getNestedGenericParameterType ( ) ;
return ResolvableType . forType ( returnType . getGenericParameterType ( ) ) . getGeneric ( 0 ) . getType ( ) ;
}
else {
type = returnType . getGenericParameterType ( ) ;
return returnType . getGenericParameterType ( ) ;
}
return type ;
}
/ * *
* @see # getProducibleMediaTypes ( HttpServletRequest , Class , Type )
* /
@SuppressWarnings ( { "unchecked" , "unused" } )
protected List < MediaType > getProducibleMediaTypes ( HttpServletRequest request , Class < ? > returnV alueClass) {
return getProducibleMediaTypes ( request , returnV alueClass, null ) ;
protected List < MediaType > getProducibleMediaTypes ( HttpServletRequest request , Class < ? > v alueClass) {
return getProducibleMediaTypes ( request , v alueClass, null ) ;
}
/ * *
@ -296,7 +293,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
@@ -296,7 +293,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
* @since 4 . 2
* /
@SuppressWarnings ( "unchecked" )
protected List < MediaType > getProducibleMediaTypes ( HttpServletRequest request , Class < ? > returnValueClass , Type returnValue Type) {
protected List < MediaType > getProducibleMediaTypes ( HttpServletRequest request , Class < ? > valueClass , Type declared Type) {
Set < MediaType > mediaTypes = ( Set < MediaType > ) request . getAttribute ( HandlerMapping . PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE ) ;
if ( ! CollectionUtils . isEmpty ( mediaTypes ) ) {
return new ArrayList < MediaType > ( mediaTypes ) ;
@ -304,12 +301,12 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
@@ -304,12 +301,12 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
else if ( ! this . allSupportedMediaTypes . isEmpty ( ) ) {
List < MediaType > result = new ArrayList < MediaType > ( ) ;
for ( HttpMessageConverter < ? > converter : this . messageConverters ) {
if ( converter instanceof GenericHttpMessageConverter & & returnValue Type ! = null ) {
if ( ( ( GenericHttpMessageConverter < ? > ) converter ) . canWrite ( returnValueType , returnV alueClass, null ) ) {
if ( converter instanceof GenericHttpMessageConverter & & declared Type ! = null ) {
if ( ( ( GenericHttpMessageConverter < ? > ) converter ) . canWrite ( declaredType , v alueClass, null ) ) {
result . addAll ( converter . getSupportedMediaTypes ( ) ) ;
}
}
else if ( converter . canWrite ( returnV alueClass, null ) ) {
else if ( converter . canWrite ( v alueClass, null ) ) {
result . addAll ( converter . getSupportedMediaTypes ( ) ) ;
}
}
@ -412,7 +409,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
@@ -412,7 +409,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
try {
mediaTypes = this . pathStrategy . resolveMediaTypeKey ( null , extension ) ;
}
catch ( HttpMediaTypeNotAcceptableException e ) {
catch ( HttpMediaTypeNotAcceptableException ex ) {
// Ignore
}
if ( CollectionUtils . isEmpty ( mediaTypes ) ) {