17
17
package org .springframework .web .servlet .mvc .method .annotation ;
18
18
19
19
import java .io .IOException ;
20
+ import java .io .InputStream ;
21
+ import java .io .PushbackInputStream ;
20
22
import java .lang .annotation .Annotation ;
21
23
import java .lang .reflect .Type ;
22
24
import java .util .ArrayList ;
33
35
import org .springframework .core .MethodParameter ;
34
36
import org .springframework .core .ResolvableType ;
35
37
import org .springframework .core .annotation .AnnotationUtils ;
38
+ import org .springframework .http .HttpHeaders ;
36
39
import org .springframework .http .HttpInputMessage ;
37
40
import org .springframework .http .InvalidMediaTypeException ;
38
41
import org .springframework .http .MediaType ;
57
60
*/
58
61
public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
59
62
63
+ private static final Object NO_VALUE = new Object ();
64
+
65
+
60
66
protected final Log logger = LogFactory .getLog (getClass ());
61
67
62
68
protected final List <HttpMessageConverter <?>> messageConverters ;
@@ -135,9 +141,8 @@ protected <T> Object readWithMessageConverters(NativeWebRequest webRequest,
135
141
* @param <T> the expected type of the argument value to be created
136
142
* @param inputMessage the HTTP input message representing the current request
137
143
* @param param the method parameter descriptor (may be {@code null})
138
- * @param targetType the type of object to create, not necessarily the same as
139
- * the method parameter type (e.g. for {@code HttpEntity<String>} method
140
- * parameter the target type is String)
144
+ * @param targetType the target type, not necessarily the same as the method
145
+ * parameter type, e.g. for {@code HttpEntity<String>}.
141
146
* @return the created method argument value
142
147
* @throws IOException if the reading from the request fails
143
148
* @throws HttpMediaTypeNotSupportedException if no suitable message converter is found
@@ -165,6 +170,9 @@ protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage,
165
170
targetClass = (Class <T >) resolvableType .resolve ();
166
171
}
167
172
173
+ inputMessage = new EmptyBodyCheckingHttpInputMessage (inputMessage );
174
+ Object body = NO_VALUE ;
175
+
168
176
for (HttpMessageConverter <?> converter : this .messageConverters ) {
169
177
Class <HttpMessageConverter <?>> converterType = (Class <HttpMessageConverter <?>>) converter .getClass ();
170
178
if (converter instanceof GenericHttpMessageConverter ) {
@@ -173,24 +181,42 @@ protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage,
173
181
if (logger .isDebugEnabled ()) {
174
182
logger .debug ("Read [" + targetType + "] as \" " + contentType + "\" with [" + converter + "]" );
175
183
}
176
- inputMessage = getAdvice ().beforeBodyRead (inputMessage , param , targetType , converterType );
177
- T body = (T ) genericConverter .read (targetType , contextClass , inputMessage );
178
- return getAdvice ().afterBodyRead (body , inputMessage , param , targetType , converterType );
184
+ if (inputMessage .getBody () != null ) {
185
+ inputMessage = getAdvice ().beforeBodyRead (inputMessage , param , targetType , converterType );
186
+ body = genericConverter .read (targetType , contextClass , inputMessage );
187
+ body = getAdvice ().afterBodyRead (body , inputMessage , param , targetType , converterType );
188
+ }
189
+ else {
190
+ body = null ;
191
+ body = getAdvice ().handleEmptyBody (body , inputMessage , param , targetType , converterType );
192
+ }
193
+ break ;
179
194
}
180
195
}
181
196
else if (targetClass != null ) {
182
197
if (converter .canRead (targetClass , contentType )) {
183
198
if (logger .isDebugEnabled ()) {
184
199
logger .debug ("Read [" + targetType + "] as \" " + contentType + "\" with [" + converter + "]" );
185
200
}
186
- inputMessage = getAdvice ().beforeBodyRead (inputMessage , param , targetType , converterType );
187
- T body = ((HttpMessageConverter <T >) converter ).read (targetClass , inputMessage );
188
- return getAdvice ().afterBodyRead (body , inputMessage , param , targetType , converterType );
201
+ if (inputMessage .getBody () != null ) {
202
+ inputMessage = getAdvice ().beforeBodyRead (inputMessage , param , targetType , converterType );
203
+ body = ((HttpMessageConverter <T >) converter ).read (targetClass , inputMessage );
204
+ body = getAdvice ().afterBodyRead (body , inputMessage , param , targetType , converterType );
205
+ }
206
+ else {
207
+ body = null ;
208
+ body = getAdvice ().handleEmptyBody (body , inputMessage , param , targetType , converterType );
209
+ }
210
+ break ;
189
211
}
190
212
}
191
213
}
192
214
193
- throw new HttpMediaTypeNotSupportedException (contentType , this .allSupportedMediaTypes );
215
+ if (body == NO_VALUE ) {
216
+ throw new HttpMediaTypeNotSupportedException (contentType , this .allSupportedMediaTypes );
217
+ }
218
+
219
+ return body ;
194
220
}
195
221
196
222
/**
@@ -240,4 +266,47 @@ protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter
240
266
return !hasBindingResult ;
241
267
}
242
268
269
+
270
+ private static class EmptyBodyCheckingHttpInputMessage implements HttpInputMessage {
271
+
272
+ private final HttpHeaders headers ;
273
+
274
+ private final InputStream body ;
275
+
276
+
277
+ public EmptyBodyCheckingHttpInputMessage (HttpInputMessage inputMessage ) throws IOException {
278
+ this .headers = inputMessage .getHeaders ();
279
+ InputStream inputStream = inputMessage .getBody ();
280
+ if (inputStream == null ) {
281
+ this .body = null ;
282
+ }
283
+ else if (inputStream .markSupported ()) {
284
+ inputStream .mark (1 );
285
+ this .body = (inputStream .read () != -1 ? inputStream : null );
286
+ inputStream .reset ();
287
+ }
288
+ else {
289
+ PushbackInputStream pushbackInputStream = new PushbackInputStream (inputStream );
290
+ int b = pushbackInputStream .read ();
291
+ if (b == -1 ) {
292
+ this .body = null ;
293
+ }
294
+ else {
295
+ this .body = pushbackInputStream ;
296
+ pushbackInputStream .unread (b );
297
+ }
298
+ }
299
+ }
300
+
301
+ @ Override
302
+ public HttpHeaders getHeaders () {
303
+ return this .headers ;
304
+ }
305
+
306
+ @ Override
307
+ public InputStream getBody () throws IOException {
308
+ return this .body ;
309
+ }
310
+ }
311
+
243
312
}
0 commit comments