22
22
import java .lang .annotation .Annotation ;
23
23
import java .lang .reflect .Type ;
24
24
import java .util .ArrayList ;
25
+ import java .util .Collection ;
25
26
import java .util .Collections ;
26
27
import java .util .EnumSet ;
27
28
import java .util .LinkedHashSet ;
28
29
import java .util .List ;
30
+ import java .util .Optional ;
29
31
import java .util .Set ;
30
32
import javax .servlet .http .HttpServletRequest ;
31
33
59
61
*
60
62
* @author Arjen Poutsma
61
63
* @author Rossen Stoyanchev
64
+ * @author Juergen Hoeller
62
65
* @since 3.1
63
66
*/
64
67
public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
@@ -128,33 +131,33 @@ protected RequestResponseBodyAdviceChain getAdvice() {
128
131
* reading from the given request.
129
132
* @param <T> the expected type of the argument value to be created
130
133
* @param webRequest the current request
131
- * @param methodParam the method argument
134
+ * @param parameter the method parameter descriptor (may be {@code null})
132
135
* @param paramType the type of the argument value to be created
133
136
* @return the created method argument value
134
137
* @throws IOException if the reading from the request fails
135
138
* @throws HttpMediaTypeNotSupportedException if no suitable message converter is found
136
139
*/
137
- protected <T > Object readWithMessageConverters (NativeWebRequest webRequest , MethodParameter methodParam ,
140
+ protected <T > Object readWithMessageConverters (NativeWebRequest webRequest , MethodParameter parameter ,
138
141
Type paramType ) throws IOException , HttpMediaTypeNotSupportedException , HttpMessageNotReadableException {
139
142
140
143
HttpInputMessage inputMessage = createInputMessage (webRequest );
141
- return readWithMessageConverters (inputMessage , methodParam , paramType );
144
+ return readWithMessageConverters (inputMessage , parameter , paramType );
142
145
}
143
146
144
147
/**
145
148
* Create the method argument value of the expected parameter type by reading
146
149
* from the given HttpInputMessage.
147
150
* @param <T> the expected type of the argument value to be created
148
151
* @param inputMessage the HTTP input message representing the current request
149
- * @param param the method parameter descriptor (may be {@code null})
152
+ * @param parameter the method parameter descriptor (may be {@code null})
150
153
* @param targetType the target type, not necessarily the same as the method
151
154
* parameter type, e.g. for {@code HttpEntity<String>}.
152
155
* @return the created method argument value
153
156
* @throws IOException if the reading from the request fails
154
157
* @throws HttpMediaTypeNotSupportedException if no suitable message converter is found
155
158
*/
156
159
@ SuppressWarnings ("unchecked" )
157
- protected <T > Object readWithMessageConverters (HttpInputMessage inputMessage , MethodParameter param ,
160
+ protected <T > Object readWithMessageConverters (HttpInputMessage inputMessage , MethodParameter parameter ,
158
161
Type targetType ) throws IOException , HttpMediaTypeNotSupportedException , HttpMessageNotReadableException {
159
162
160
163
MediaType contentType ;
@@ -170,11 +173,11 @@ protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, Me
170
173
contentType = MediaType .APPLICATION_OCTET_STREAM ;
171
174
}
172
175
173
- Class <?> contextClass = (param != null ? param .getContainingClass () : null );
176
+ Class <?> contextClass = (parameter != null ? parameter .getContainingClass () : null );
174
177
Class <T > targetClass = (targetType instanceof Class ? (Class <T >) targetType : null );
175
178
if (targetClass == null ) {
176
- ResolvableType resolvableType = (param != null ?
177
- ResolvableType .forMethodParameter (param ) : ResolvableType .forType (targetType ));
179
+ ResolvableType resolvableType = (parameter != null ?
180
+ ResolvableType .forMethodParameter (parameter ) : ResolvableType .forType (targetType ));
178
181
targetClass = (Class <T >) resolvableType .resolve ();
179
182
}
180
183
@@ -193,13 +196,12 @@ protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, Me
193
196
logger .debug ("Read [" + targetType + "] as \" " + contentType + "\" with [" + converter + "]" );
194
197
}
195
198
if (inputMessage .getBody () != null ) {
196
- inputMessage = getAdvice ().beforeBodyRead (inputMessage , param , targetType , converterType );
199
+ inputMessage = getAdvice ().beforeBodyRead (inputMessage , parameter , targetType , converterType );
197
200
body = genericConverter .read (targetType , contextClass , inputMessage );
198
- body = getAdvice ().afterBodyRead (body , inputMessage , param , targetType , converterType );
201
+ body = getAdvice ().afterBodyRead (body , inputMessage , parameter , targetType , converterType );
199
202
}
200
203
else {
201
- body = null ;
202
- body = getAdvice ().handleEmptyBody (body , inputMessage , param , targetType , converterType );
204
+ body = getAdvice ().handleEmptyBody (null , inputMessage , parameter , targetType , converterType );
203
205
}
204
206
break ;
205
207
}
@@ -210,13 +212,12 @@ else if (targetClass != null) {
210
212
logger .debug ("Read [" + targetType + "] as \" " + contentType + "\" with [" + converter + "]" );
211
213
}
212
214
if (inputMessage .getBody () != null ) {
213
- inputMessage = getAdvice ().beforeBodyRead (inputMessage , param , targetType , converterType );
215
+ inputMessage = getAdvice ().beforeBodyRead (inputMessage , parameter , targetType , converterType );
214
216
body = ((HttpMessageConverter <T >) converter ).read (targetClass , inputMessage );
215
- body = getAdvice ().afterBodyRead (body , inputMessage , param , targetType , converterType );
217
+ body = getAdvice ().afterBodyRead (body , inputMessage , parameter , targetType , converterType );
216
218
}
217
219
else {
218
- body = null ;
219
- body = getAdvice ().handleEmptyBody (body , inputMessage , param , targetType , converterType );
220
+ body = getAdvice ().handleEmptyBody (null , inputMessage , parameter , targetType , converterType );
220
221
}
221
222
break ;
222
223
}
@@ -254,12 +255,12 @@ protected ServletServerHttpRequest createInputMessage(NativeWebRequest webReques
254
255
* Spring's {@link org.springframework.validation.annotation.Validated},
255
256
* and custom annotations whose name starts with "Valid".
256
257
* @param binder the DataBinder to be used
257
- * @param methodParam the method parameter
258
- * @see #isBindExceptionRequired
258
+ * @param parameter the method parameter descriptor
259
259
* @since 4.1.5
260
+ * @see #isBindExceptionRequired
260
261
*/
261
- protected void validateIfApplicable (WebDataBinder binder , MethodParameter methodParam ) {
262
- Annotation [] annotations = methodParam .getParameterAnnotations ();
262
+ protected void validateIfApplicable (WebDataBinder binder , MethodParameter parameter ) {
263
+ Annotation [] annotations = parameter .getParameterAnnotations ();
263
264
for (Annotation ann : annotations ) {
264
265
Validated validatedAnn = AnnotationUtils .getAnnotation (ann , Validated .class );
265
266
if (validatedAnn != null || ann .annotationType ().getSimpleName ().startsWith ("Valid" )) {
@@ -274,17 +275,37 @@ protected void validateIfApplicable(WebDataBinder binder, MethodParameter method
274
275
/**
275
276
* Whether to raise a fatal bind exception on validation errors.
276
277
* @param binder the data binder used to perform data binding
277
- * @param methodParam the method argument
278
+ * @param parameter the method parameter descriptor
278
279
* @return {@code true} if the next method argument is not of type {@link Errors}
279
280
* @since 4.1.5
280
281
*/
281
- protected boolean isBindExceptionRequired (WebDataBinder binder , MethodParameter methodParam ) {
282
- int i = methodParam .getParameterIndex ();
283
- Class <?>[] paramTypes = methodParam .getMethod ().getParameterTypes ();
282
+ protected boolean isBindExceptionRequired (WebDataBinder binder , MethodParameter parameter ) {
283
+ int i = parameter .getParameterIndex ();
284
+ Class <?>[] paramTypes = parameter .getMethod ().getParameterTypes ();
284
285
boolean hasBindingResult = (paramTypes .length > (i + 1 ) && Errors .class .isAssignableFrom (paramTypes [i + 1 ]));
285
286
return !hasBindingResult ;
286
287
}
287
288
289
+ /**
290
+ * Adapt the given argument against the method parameter, if necessary.
291
+ * @param arg the resolved argument
292
+ * @param parameter the method parameter descriptor
293
+ * @return the adapted argument, or the original resolved argument as-is
294
+ * @since 4.3.5
295
+ */
296
+ protected Object adaptArgumentIfNecessary (Object arg , MethodParameter parameter ) {
297
+ if (parameter .getParameterType () == Optional .class ) {
298
+ if (arg == null || (arg instanceof Collection && ((Collection ) arg ).isEmpty ()) ||
299
+ (arg instanceof Object [] && ((Object []) arg ).length == 0 )) {
300
+ return Optional .empty ();
301
+ }
302
+ else {
303
+ return Optional .of (arg );
304
+ }
305
+ }
306
+ return arg ;
307
+ }
308
+
288
309
289
310
private static class EmptyBodyCheckingHttpInputMessage implements HttpInputMessage {
290
311
0 commit comments