@@ -116,9 +116,10 @@ protected <T> void writeWithMessageConverters(T returnValue, MethodParameter ret
116
116
throws IOException , HttpMediaTypeNotAcceptableException {
117
117
118
118
Class <?> returnValueClass = getReturnValueType (returnValue , returnType );
119
+ Type returnValueType = getGenericType (returnType );
119
120
HttpServletRequest servletRequest = inputMessage .getServletRequest ();
120
121
List <MediaType > requestedMediaTypes = getAcceptableMediaTypes (servletRequest );
121
- List <MediaType > producibleMediaTypes = getProducibleMediaTypes (servletRequest , returnValueClass );
122
+ List <MediaType > producibleMediaTypes = getProducibleMediaTypes (servletRequest , returnValueClass , returnValueType );
122
123
123
124
Assert .isTrue (returnValue == null || !producibleMediaTypes .isEmpty (),
124
125
"No converter found for return value of type: " + returnValueClass );
@@ -156,25 +157,30 @@ else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICAT
156
157
if (selectedMediaType != null ) {
157
158
selectedMediaType = selectedMediaType .removeQualityValue ();
158
159
for (HttpMessageConverter <?> messageConverter : this .messageConverters ) {
159
- if (messageConverter .canWrite (returnValueClass , selectedMediaType )) {
160
+ if (messageConverter instanceof GenericHttpMessageConverter ) {
161
+ if (((GenericHttpMessageConverter <T >) messageConverter ).canWrite (returnValueType ,
162
+ returnValueClass , selectedMediaType )) {
163
+ returnValue = (T ) getAdvice ().beforeBodyWrite (returnValue , returnType , selectedMediaType ,
164
+ (Class <? extends HttpMessageConverter <?>>) messageConverter .getClass (),
165
+ inputMessage , outputMessage );
166
+ if (returnValue != null ) {
167
+ ((GenericHttpMessageConverter <T >) messageConverter ).write (returnValue ,
168
+ returnValueType , selectedMediaType , outputMessage );
169
+ if (logger .isDebugEnabled ()) {
170
+ logger .debug ("Written [" + returnValue + "] as \" " +
171
+ selectedMediaType + "\" using [" + messageConverter + "]" );
172
+ }
173
+ }
174
+ return ;
175
+ }
176
+ }
177
+ else if (messageConverter .canWrite (returnValueClass , selectedMediaType )) {
160
178
returnValue = (T ) getAdvice ().beforeBodyWrite (returnValue , returnType , selectedMediaType ,
161
179
(Class <? extends HttpMessageConverter <?>>) messageConverter .getClass (),
162
180
inputMessage , outputMessage );
163
181
if (returnValue != null ) {
164
- if (messageConverter instanceof GenericHttpMessageConverter ) {
165
- Type type ;
166
- if (HttpEntity .class .isAssignableFrom (returnType .getParameterType ())) {
167
- returnType .increaseNestingLevel ();
168
- type = returnType .getNestedGenericParameterType ();
169
- }
170
- else {
171
- type = returnType .getGenericParameterType ();
172
- }
173
- ((GenericHttpMessageConverter <T >) messageConverter ).write (returnValue , type , selectedMediaType , outputMessage );
174
- }
175
- else {
176
- ((HttpMessageConverter <T >) messageConverter ).write (returnValue , selectedMediaType , outputMessage );
177
- }
182
+ ((HttpMessageConverter <T >) messageConverter ).write (returnValue ,
183
+ selectedMediaType , outputMessage );
178
184
if (logger .isDebugEnabled ()) {
179
185
logger .debug ("Written [" + returnValue + "] as \" " +
180
186
selectedMediaType + "\" using [" + messageConverter + "]" );
@@ -200,24 +206,54 @@ protected Class<?> getReturnValueType(Object returnValue, MethodParameter return
200
206
return (returnValue != null ? returnValue .getClass () : returnType .getParameterType ());
201
207
}
202
208
209
+ /**
210
+ * Return the generic type of the {@code returnType} (or of the nested type if it is
211
+ * a {@link HttpEntity}).
212
+ */
213
+ private Type getGenericType (MethodParameter returnType ) {
214
+ Type type ;
215
+ if (HttpEntity .class .isAssignableFrom (returnType .getParameterType ())) {
216
+ returnType .increaseNestingLevel ();
217
+ type = returnType .getNestedGenericParameterType ();
218
+ }
219
+ else {
220
+ type = returnType .getGenericParameterType ();
221
+ }
222
+ return type ;
223
+ }
224
+
225
+ /**
226
+ * @see #getProducibleMediaTypes(HttpServletRequest, Class, Type)
227
+ */
228
+ @ SuppressWarnings ("unchecked" )
229
+ protected List <MediaType > getProducibleMediaTypes (HttpServletRequest request , Class <?> returnValueClass ) {
230
+ return getProducibleMediaTypes (request , returnValueClass , null );
231
+ }
232
+
203
233
/**
204
234
* Returns the media types that can be produced:
205
235
* <ul>
206
236
* <li>The producible media types specified in the request mappings, or
207
237
* <li>Media types of configured converters that can write the specific return value, or
208
238
* <li>{@link MediaType#ALL}
209
239
* </ul>
240
+ * @since 4.2
210
241
*/
211
242
@ SuppressWarnings ("unchecked" )
212
- protected List <MediaType > getProducibleMediaTypes (HttpServletRequest request , Class <?> returnValueClass ) {
243
+ protected List <MediaType > getProducibleMediaTypes (HttpServletRequest request , Class <?> returnValueClass , Type returnValueType ) {
213
244
Set <MediaType > mediaTypes = (Set <MediaType >) request .getAttribute (HandlerMapping .PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE );
214
245
if (!CollectionUtils .isEmpty (mediaTypes )) {
215
246
return new ArrayList <MediaType >(mediaTypes );
216
247
}
217
248
else if (!this .allSupportedMediaTypes .isEmpty ()) {
218
249
List <MediaType > result = new ArrayList <MediaType >();
219
250
for (HttpMessageConverter <?> converter : this .messageConverters ) {
220
- if (converter .canWrite (returnValueClass , null )) {
251
+ if (converter instanceof GenericHttpMessageConverter && returnValueType != null ) {
252
+ if (((GenericHttpMessageConverter <?>) converter ).canWrite (returnValueType , returnValueClass , null )) {
253
+ result .addAll (converter .getSupportedMediaTypes ());
254
+ }
255
+ }
256
+ else if (converter .canWrite (returnValueClass , null )) {
221
257
result .addAll (converter .getSupportedMediaTypes ());
222
258
}
223
259
}
0 commit comments