1
1
/*
2
- * Copyright 2002-2015 the original author or authors.
2
+ * Copyright 2002-2016 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
30
30
import javax .servlet .http .HttpServletResponse ;
31
31
32
32
import org .springframework .core .MethodParameter ;
33
+ import org .springframework .core .ResolvableType ;
33
34
import org .springframework .http .HttpEntity ;
34
35
import org .springframework .http .HttpHeaders ;
35
36
import org .springframework .http .HttpOutputMessage ;
62
63
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
63
64
implements HandlerMethodReturnValueHandler {
64
65
65
- private static final MediaType MEDIA_TYPE_APPLICATION = new MediaType ("application" );
66
-
67
- private static final UrlPathHelper RAW_URL_PATH_HELPER = new UrlPathHelper ();
68
-
69
- private static final UrlPathHelper DECODING_URL_PATH_HELPER = new UrlPathHelper ();
70
-
71
- static {
72
- RAW_URL_PATH_HELPER .setRemoveSemicolonContent (false );
73
- RAW_URL_PATH_HELPER .setUrlDecode (false );
74
- }
75
-
76
66
/* Extensions associated with the built-in message converters */
77
67
private static final Set <String > WHITELISTED_EXTENSIONS = new HashSet <String >(Arrays .asList (
78
68
"txt" , "text" , "yml" , "properties" , "csv" ,
@@ -82,6 +72,17 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
82
72
private static final Set <String > WHITELISTED_MEDIA_BASE_TYPES = new HashSet <String >(
83
73
Arrays .asList ("audio" , "image" , "video" ));
84
74
75
+ private static final MediaType MEDIA_TYPE_APPLICATION = new MediaType ("application" );
76
+
77
+ private static final UrlPathHelper DECODING_URL_PATH_HELPER = new UrlPathHelper ();
78
+
79
+ private static final UrlPathHelper RAW_URL_PATH_HELPER = new UrlPathHelper ();
80
+
81
+ static {
82
+ RAW_URL_PATH_HELPER .setRemoveSemicolonContent (false );
83
+ RAW_URL_PATH_HELPER .setUrlDecode (false );
84
+ }
85
+
85
86
86
87
private final ContentNegotiationManager contentNegotiationManager ;
87
88
@@ -145,17 +146,17 @@ protected ServletServerHttpResponse createOutputMessage(NativeWebRequest webRequ
145
146
* Writes the given return value to the given web request. Delegates to
146
147
* {@link #writeWithMessageConverters(Object, MethodParameter, ServletServerHttpRequest, ServletServerHttpResponse)}
147
148
*/
148
- protected <T > void writeWithMessageConverters (T returnValue , MethodParameter returnType , NativeWebRequest webRequest )
149
+ protected <T > void writeWithMessageConverters (T value , MethodParameter returnType , NativeWebRequest webRequest )
149
150
throws IOException , HttpMediaTypeNotAcceptableException , HttpMessageNotWritableException {
150
151
151
152
ServletServerHttpRequest inputMessage = createInputMessage (webRequest );
152
153
ServletServerHttpResponse outputMessage = createOutputMessage (webRequest );
153
- writeWithMessageConverters (returnValue , returnType , inputMessage , outputMessage );
154
+ writeWithMessageConverters (value , returnType , inputMessage , outputMessage );
154
155
}
155
156
156
157
/**
157
158
* Writes the given return type to the given output message.
158
- * @param returnValue the value to write to the output message
159
+ * @param value the value to write to the output message
159
160
* @param returnType the type of the value
160
161
* @param inputMessage the input messages. Used to inspect the {@code Accept} header.
161
162
* @param outputMessage the output message to write to
@@ -164,18 +165,18 @@ protected <T> void writeWithMessageConverters(T returnValue, MethodParameter ret
164
165
* the request cannot be met by the message converters
165
166
*/
166
167
@ SuppressWarnings ("unchecked" )
167
- protected <T > void writeWithMessageConverters (T returnValue , MethodParameter returnType ,
168
+ protected <T > void writeWithMessageConverters (T value , MethodParameter returnType ,
168
169
ServletServerHttpRequest inputMessage , ServletServerHttpResponse outputMessage )
169
170
throws IOException , HttpMediaTypeNotAcceptableException , HttpMessageNotWritableException {
170
171
171
- Class <?> returnValueClass = getReturnValueType (returnValue , returnType );
172
- Type returnValueType = getGenericType (returnType );
173
- HttpServletRequest servletRequest = inputMessage .getServletRequest ();
174
- List <MediaType > requestedMediaTypes = getAcceptableMediaTypes (servletRequest );
175
- List <MediaType > producibleMediaTypes = getProducibleMediaTypes (servletRequest , returnValueClass , returnValueType );
172
+ Class <?> valueType = getReturnValueType (value , returnType );
173
+ Type declaredType = getGenericType (returnType );
174
+ HttpServletRequest request = inputMessage .getServletRequest ();
175
+ List <MediaType > requestedMediaTypes = getAcceptableMediaTypes (request );
176
+ List <MediaType > producibleMediaTypes = getProducibleMediaTypes (request , valueType , declaredType );
176
177
177
- if (returnValue != null && producibleMediaTypes .isEmpty ()) {
178
- throw new IllegalArgumentException ("No converter found for return value of type: " + returnValueClass );
178
+ if (value != null && producibleMediaTypes .isEmpty ()) {
179
+ throw new IllegalArgumentException ("No converter found for return value of type: " + valueType );
179
180
}
180
181
181
182
Set <MediaType > compatibleMediaTypes = new LinkedHashSet <MediaType >();
@@ -187,7 +188,7 @@ protected <T> void writeWithMessageConverters(T returnValue, MethodParameter ret
187
188
}
188
189
}
189
190
if (compatibleMediaTypes .isEmpty ()) {
190
- if (returnValue != null ) {
191
+ if (value != null ) {
191
192
throw new HttpMediaTypeNotAcceptableException (producibleMediaTypes );
192
193
}
193
194
return ;
@@ -212,78 +213,74 @@ else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICAT
212
213
selectedMediaType = selectedMediaType .removeQualityValue ();
213
214
for (HttpMessageConverter <?> messageConverter : this .messageConverters ) {
214
215
if (messageConverter instanceof GenericHttpMessageConverter ) {
215
- if (((GenericHttpMessageConverter <T >) messageConverter ).canWrite (returnValueType ,
216
- returnValueClass , selectedMediaType )) {
217
- returnValue = (T ) getAdvice ().beforeBodyWrite (returnValue , returnType , selectedMediaType ,
216
+ if (((GenericHttpMessageConverter <T >) messageConverter ).canWrite (
217
+ declaredType , valueType , selectedMediaType )) {
218
+ value = (T ) getAdvice ().beforeBodyWrite (value , returnType , selectedMediaType ,
218
219
(Class <? extends HttpMessageConverter <?>>) messageConverter .getClass (),
219
220
inputMessage , outputMessage );
220
- if (returnValue != null ) {
221
+ if (value != null ) {
221
222
addContentDispositionHeader (inputMessage , outputMessage );
222
- ((GenericHttpMessageConverter <T >) messageConverter ).write (returnValue ,
223
- returnValueType , selectedMediaType , outputMessage );
223
+ ((GenericHttpMessageConverter <T >) messageConverter ).write (
224
+ value , declaredType , selectedMediaType , outputMessage );
224
225
if (logger .isDebugEnabled ()) {
225
- logger .debug ("Written [" + returnValue + "] as \" " +
226
- selectedMediaType + "\" using [" + messageConverter + "]" );
226
+ logger .debug ("Written [" + value + "] as \" " + selectedMediaType +
227
+ "\" using [" + messageConverter + "]" );
227
228
}
228
229
}
229
230
return ;
230
231
}
231
232
}
232
- else if (messageConverter .canWrite (returnValueClass , selectedMediaType )) {
233
- returnValue = (T ) getAdvice ().beforeBodyWrite (returnValue , returnType , selectedMediaType ,
233
+ else if (messageConverter .canWrite (valueType , selectedMediaType )) {
234
+ value = (T ) getAdvice ().beforeBodyWrite (value , returnType , selectedMediaType ,
234
235
(Class <? extends HttpMessageConverter <?>>) messageConverter .getClass (),
235
236
inputMessage , outputMessage );
236
- if (returnValue != null ) {
237
+ if (value != null ) {
237
238
addContentDispositionHeader (inputMessage , outputMessage );
238
- ((HttpMessageConverter <T >) messageConverter ).write (returnValue ,
239
- selectedMediaType , outputMessage );
239
+ ((HttpMessageConverter <T >) messageConverter ).write (value , selectedMediaType , outputMessage );
240
240
if (logger .isDebugEnabled ()) {
241
- logger .debug ("Written [" + returnValue + "] as \" " +
242
- selectedMediaType + "\" using [" + messageConverter + "]" );
241
+ logger .debug ("Written [" + value + "] as \" " + selectedMediaType +
242
+ "\" using [" + messageConverter + "]" );
243
243
}
244
244
}
245
245
return ;
246
246
}
247
247
}
248
248
}
249
249
250
- if (returnValue != null ) {
250
+ if (value != null ) {
251
251
throw new HttpMediaTypeNotAcceptableException (this .allSupportedMediaTypes );
252
252
}
253
253
}
254
254
255
255
/**
256
- * Return the type of the value to be written to the response. Typically this
257
- * is a simple check via getClass on the returnValue but if the returnValue is
258
- * null, then the returnType needs to be examined possibly including generic
259
- * type determination (e.g. {@code ResponseEntity<T>}).
256
+ * Return the type of the value to be written to the response. Typically this is
257
+ * a simple check via getClass on the value but if the value is null, then the
258
+ * return type needs to be examined possibly including generic type determination
259
+ * (e.g. {@code ResponseEntity<T>}).
260
260
*/
261
- protected Class <?> getReturnValueType (Object returnValue , MethodParameter returnType ) {
262
- return (returnValue != null ? returnValue .getClass () : returnType .getParameterType ());
261
+ protected Class <?> getReturnValueType (Object value , MethodParameter returnType ) {
262
+ return (value != null ? value .getClass () : returnType .getParameterType ());
263
263
}
264
264
265
265
/**
266
- * Return the generic type of the {@code returnType} (or of the nested type if it is
267
- * a {@link HttpEntity}).
266
+ * Return the generic type of the {@code returnType} (or of the nested type
267
+ * if it is an {@link HttpEntity}).
268
268
*/
269
269
private Type getGenericType (MethodParameter returnType ) {
270
- Type type ;
271
270
if (HttpEntity .class .isAssignableFrom (returnType .getParameterType ())) {
272
- returnType .increaseNestingLevel ();
273
- type = returnType .getNestedGenericParameterType ();
271
+ return ResolvableType .forType (returnType .getGenericParameterType ()).getGeneric (0 ).getType ();
274
272
}
275
273
else {
276
- type = returnType .getGenericParameterType ();
274
+ return returnType .getGenericParameterType ();
277
275
}
278
- return type ;
279
276
}
280
277
281
278
/**
282
279
* @see #getProducibleMediaTypes(HttpServletRequest, Class, Type)
283
280
*/
284
281
@ SuppressWarnings ({"unchecked" , "unused" })
285
- protected List <MediaType > getProducibleMediaTypes (HttpServletRequest request , Class <?> returnValueClass ) {
286
- return getProducibleMediaTypes (request , returnValueClass , null );
282
+ protected List <MediaType > getProducibleMediaTypes (HttpServletRequest request , Class <?> valueClass ) {
283
+ return getProducibleMediaTypes (request , valueClass , null );
287
284
}
288
285
289
286
/**
@@ -296,20 +293,20 @@ protected List<MediaType> getProducibleMediaTypes(HttpServletRequest request, Cl
296
293
* @since 4.2
297
294
*/
298
295
@ SuppressWarnings ("unchecked" )
299
- protected List <MediaType > getProducibleMediaTypes (HttpServletRequest request , Class <?> returnValueClass , Type returnValueType ) {
296
+ protected List <MediaType > getProducibleMediaTypes (HttpServletRequest request , Class <?> valueClass , Type declaredType ) {
300
297
Set <MediaType > mediaTypes = (Set <MediaType >) request .getAttribute (HandlerMapping .PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE );
301
298
if (!CollectionUtils .isEmpty (mediaTypes )) {
302
299
return new ArrayList <MediaType >(mediaTypes );
303
300
}
304
301
else if (!this .allSupportedMediaTypes .isEmpty ()) {
305
302
List <MediaType > result = new ArrayList <MediaType >();
306
303
for (HttpMessageConverter <?> converter : this .messageConverters ) {
307
- if (converter instanceof GenericHttpMessageConverter && returnValueType != null ) {
308
- if (((GenericHttpMessageConverter <?>) converter ).canWrite (returnValueType , returnValueClass , null )) {
304
+ if (converter instanceof GenericHttpMessageConverter && declaredType != null ) {
305
+ if (((GenericHttpMessageConverter <?>) converter ).canWrite (declaredType , valueClass , null )) {
309
306
result .addAll (converter .getSupportedMediaTypes ());
310
307
}
311
308
}
312
- else if (converter .canWrite (returnValueClass , null )) {
309
+ else if (converter .canWrite (valueClass , null )) {
313
310
result .addAll (converter .getSupportedMediaTypes ());
314
311
}
315
312
}
@@ -412,7 +409,7 @@ private boolean safeMediaTypesForExtension(String extension) {
412
409
try {
413
410
mediaTypes = this .pathStrategy .resolveMediaTypeKey (null , extension );
414
411
}
415
- catch (HttpMediaTypeNotAcceptableException e ) {
412
+ catch (HttpMediaTypeNotAcceptableException ex ) {
416
413
// Ignore
417
414
}
418
415
if (CollectionUtils .isEmpty (mediaTypes )) {
0 commit comments