19
19
import java .lang .reflect .Method ;
20
20
import java .util .List ;
21
21
import java .util .Locale ;
22
+ import java .util .function .Predicate ;
22
23
23
24
import org .springframework .context .MessageSource ;
25
+ import org .springframework .core .MethodParameter ;
24
26
import org .springframework .http .HttpStatus ;
27
+ import org .springframework .lang .Nullable ;
28
+ import org .springframework .util .Assert ;
25
29
import org .springframework .validation .method .MethodValidationResult ;
30
+ import org .springframework .validation .method .ParameterErrors ;
26
31
import org .springframework .validation .method .ParameterValidationResult ;
32
+ import org .springframework .web .bind .annotation .CookieValue ;
33
+ import org .springframework .web .bind .annotation .MatrixVariable ;
34
+ import org .springframework .web .bind .annotation .ModelAttribute ;
35
+ import org .springframework .web .bind .annotation .PathVariable ;
36
+ import org .springframework .web .bind .annotation .RequestBody ;
37
+ import org .springframework .web .bind .annotation .RequestHeader ;
38
+ import org .springframework .web .bind .annotation .RequestParam ;
39
+ import org .springframework .web .bind .annotation .RequestPart ;
27
40
import org .springframework .web .server .ResponseStatusException ;
28
41
import org .springframework .web .util .BindErrorUtils ;
29
42
@@ -43,10 +56,24 @@ public class HandlerMethodValidationException extends ResponseStatusException im
43
56
44
57
private final MethodValidationResult validationResult ;
45
58
59
+ private final Predicate <MethodParameter > modelAttribitePredicate ;
60
+
61
+ private final Predicate <MethodParameter > requestParamPredicate ;
62
+
46
63
47
64
public HandlerMethodValidationException (MethodValidationResult validationResult ) {
65
+ this (validationResult ,
66
+ param -> param .hasParameterAnnotation (ModelAttribute .class ),
67
+ param -> param .hasParameterAnnotation (RequestParam .class ));
68
+ }
69
+
70
+ public HandlerMethodValidationException (MethodValidationResult validationResult ,
71
+ Predicate <MethodParameter > modelAttribitePredicate , Predicate <MethodParameter > requestParamPredicate ) {
72
+
48
73
super (initHttpStatus (validationResult ), "Validation failure" , null , null , null );
49
74
this .validationResult = validationResult ;
75
+ this .modelAttribitePredicate = modelAttribitePredicate ;
76
+ this .requestParamPredicate = requestParamPredicate ;
50
77
}
51
78
52
79
private static HttpStatus initHttpStatus (MethodValidationResult validationResult ) {
@@ -84,4 +111,133 @@ public List<ParameterValidationResult> getAllValidationResults() {
84
111
return this .validationResult .getAllValidationResults ();
85
112
}
86
113
114
+ /**
115
+ * Provide a {@link Visitor Visitor} to handle {@link ParameterValidationResult}s
116
+ * through callback methods organized by controller method parameter type.
117
+ */
118
+ public void visitResults (Visitor visitor ) {
119
+ for (ParameterValidationResult result : getAllValidationResults ()) {
120
+ MethodParameter param = result .getMethodParameter ();
121
+ CookieValue cookieValue = param .getParameterAnnotation (CookieValue .class );
122
+ if (cookieValue != null ) {
123
+ visitor .cookieValue (cookieValue , result );
124
+ continue ;
125
+ }
126
+ MatrixVariable matrixVariable = param .getParameterAnnotation (MatrixVariable .class );
127
+ if (matrixVariable != null ) {
128
+ visitor .matrixVariable (matrixVariable , result );
129
+ continue ;
130
+ }
131
+ if (this .modelAttribitePredicate .test (param )) {
132
+ ModelAttribute modelAttribute = param .getParameterAnnotation (ModelAttribute .class );
133
+ visitor .modelAttribute (modelAttribute , asErrors (result ));
134
+ continue ;
135
+ }
136
+ PathVariable pathVariable = param .getParameterAnnotation (PathVariable .class );
137
+ if (pathVariable != null ) {
138
+ visitor .pathVariable (pathVariable , result );
139
+ continue ;
140
+ }
141
+ RequestBody requestBody = param .getParameterAnnotation (RequestBody .class );
142
+ if (requestBody != null ) {
143
+ visitor .requestBody (requestBody , asErrors (result ));
144
+ continue ;
145
+ }
146
+ RequestHeader requestHeader = param .getParameterAnnotation (RequestHeader .class );
147
+ if (requestHeader != null ) {
148
+ visitor .requestHeader (requestHeader , result );
149
+ continue ;
150
+ }
151
+ if (this .requestParamPredicate .test (param )) {
152
+ RequestParam requestParam = param .getParameterAnnotation (RequestParam .class );
153
+ visitor .requestParam (requestParam , result );
154
+ continue ;
155
+ }
156
+ RequestPart requestPart = param .getParameterAnnotation (RequestPart .class );
157
+ if (requestPart != null ) {
158
+ visitor .requestPart (requestPart , asErrors (result ));
159
+ continue ;
160
+ }
161
+ visitor .other (result );
162
+ }
163
+ }
164
+
165
+ private static ParameterErrors asErrors (ParameterValidationResult result ) {
166
+ Assert .state (result instanceof ParameterErrors , "Expected ParameterErrors" );
167
+ return (ParameterErrors ) result ;
168
+ }
169
+
170
+
171
+ /**
172
+ * Contract to handle validation results with callbacks by controller method
173
+ * parameter type, with {@link #other} serving as the fallthrough.
174
+ */
175
+ public interface Visitor {
176
+
177
+ /**
178
+ * Handle results for {@code @CookieValue} method parameters.
179
+ * @param cookieValue the annotation declared on the parameter
180
+ * @param result the validation result
181
+ */
182
+ void cookieValue (CookieValue cookieValue , ParameterValidationResult result );
183
+
184
+ /**
185
+ * Handle results for {@code @MatrixVariable} method parameters.
186
+ * @param matrixVariable the annotation declared on the parameter
187
+ * @param result the validation result
188
+ */
189
+ void matrixVariable (MatrixVariable matrixVariable , ParameterValidationResult result );
190
+
191
+ /**
192
+ * Handle results for {@code @ModelAttribute} method parameters.
193
+ * @param modelAttribute the optional {@code ModelAttribute} annotation,
194
+ * possibly {@code null} if the method parameter is declared without it.
195
+ * @param errors the validation errors
196
+ */
197
+ void modelAttribute (@ Nullable ModelAttribute modelAttribute , ParameterErrors errors );
198
+
199
+ /**
200
+ * Handle results for {@code @PathVariable} method parameters.
201
+ * @param pathVariable the annotation declared on the parameter
202
+ * @param result the validation result
203
+ */
204
+ void pathVariable (PathVariable pathVariable , ParameterValidationResult result );
205
+
206
+ /**
207
+ * Handle results for {@code @RequestBody} method parameters.
208
+ * @param requestBody the annotation declared on the parameter
209
+ * @param errors the validation error
210
+ */
211
+ void requestBody (RequestBody requestBody , ParameterErrors errors );
212
+
213
+ /**
214
+ * Handle results for {@code @RequestHeader} method parameters.
215
+ * @param requestHeader the annotation declared on the parameter
216
+ * @param result the validation result
217
+ */
218
+ void requestHeader (RequestHeader requestHeader , ParameterValidationResult result );
219
+
220
+ /**
221
+ * Handle results for {@code @RequestParam} method parameters.
222
+ * @param requestParam the optional {@code RequestParam} annotation,
223
+ * possibly {@code null} if the method parameter is declared without it.
224
+ * @param result the validation result
225
+ */
226
+ void requestParam (@ Nullable RequestParam requestParam , ParameterValidationResult result );
227
+
228
+ /**
229
+ * Handle results for {@code @RequestPart} method parameters.
230
+ * @param requestPart the annotation declared on the parameter
231
+ * @param errors the validation errors
232
+ */
233
+ void requestPart (RequestPart requestPart , ParameterErrors errors );
234
+
235
+ /**
236
+ * Handle other results that aren't any of the above.
237
+ * @param result the validation result
238
+ */
239
+ void other (ParameterValidationResult result );
240
+
241
+ }
242
+
87
243
}
0 commit comments