15
15
*/
16
16
package org .springframework .data .web ;
17
17
18
- import static org .springframework .data .web .SpringDataAnnotationUtils .*;
19
-
20
- import java .lang .reflect .Method ;
21
- import java .util .Optional ;
22
-
23
- import org .springframework .beans .factory .annotation .Qualifier ;
24
18
import org .springframework .core .MethodParameter ;
25
19
import org .springframework .data .domain .PageRequest ;
26
20
import org .springframework .data .domain .Pageable ;
27
21
import org .springframework .data .domain .Sort ;
28
22
import org .springframework .lang .Nullable ;
29
- import org .springframework .util .Assert ;
30
- import org .springframework .util .StringUtils ;
31
23
import org .springframework .web .bind .support .WebDataBinderFactory ;
32
24
import org .springframework .web .context .request .NativeWebRequest ;
33
25
import org .springframework .web .method .support .ModelAndViewContainer ;
36
28
* Extracts paging information from web requests and thus allows injecting {@link Pageable} instances into controller
37
29
* methods. Request properties to be parsed can be configured. Default configuration uses request parameters beginning
38
30
* with {@link #DEFAULT_PAGE_PARAMETER}{@link #DEFAULT_QUALIFIER_DELIMITER}.
39
- *
31
+ *
40
32
* @since 1.6
41
33
* @author Oliver Gierke
42
34
* @author Nick Williams
43
35
* @author Mark Paluch
44
36
* @author Christoph Strobl
45
37
*/
46
- public class PageableHandlerMethodArgumentResolver implements PageableArgumentResolver {
38
+ public class PageableHandlerMethodArgumentResolver extends PageableHandlerMethodArgumentResolverSupport
39
+ implements PageableArgumentResolver {
47
40
48
41
private static final SortHandlerMethodArgumentResolver DEFAULT_SORT_RESOLVER = new SortHandlerMethodArgumentResolver ();
49
- private static final String INVALID_DEFAULT_PAGE_SIZE = "Invalid default page size configured for method %s! Must not be less than one!" ;
50
-
51
- private static final String DEFAULT_PAGE_PARAMETER = "page" ;
52
- private static final String DEFAULT_SIZE_PARAMETER = "size" ;
53
- private static final String DEFAULT_PREFIX = "" ;
54
- private static final String DEFAULT_QUALIFIER_DELIMITER = "_" ;
55
- private static final int DEFAULT_MAX_PAGE_SIZE = 2000 ;
56
- static final Pageable DEFAULT_PAGE_REQUEST = PageRequest .of (0 , 20 );
57
-
58
- private Pageable fallbackPageable = DEFAULT_PAGE_REQUEST ;
59
42
private SortArgumentResolver sortResolver ;
60
- private String pageParameterName = DEFAULT_PAGE_PARAMETER ;
61
- private String sizeParameterName = DEFAULT_SIZE_PARAMETER ;
62
- private String prefix = DEFAULT_PREFIX ;
63
- private String qualifierDelimiter = DEFAULT_QUALIFIER_DELIMITER ;
64
- private int maxPageSize = DEFAULT_MAX_PAGE_SIZE ;
65
- private boolean oneIndexedParameters = false ;
66
43
67
44
/**
68
45
* Constructs an instance of this resolved with a default {@link SortHandlerMethodArgumentResolver}.
@@ -73,7 +50,7 @@ public PageableHandlerMethodArgumentResolver() {
73
50
74
51
/**
75
52
* Constructs an instance of this resolver with the specified {@link SortHandlerMethodArgumentResolver}.
76
- *
53
+ *
77
54
* @param sortResolver the sort resolver to use
78
55
*/
79
56
public PageableHandlerMethodArgumentResolver (SortHandlerMethodArgumentResolver sortResolver ) {
@@ -90,139 +67,7 @@ public PageableHandlerMethodArgumentResolver(@Nullable SortArgumentResolver sort
90
67
this .sortResolver = sortResolver == null ? DEFAULT_SORT_RESOLVER : sortResolver ;
91
68
}
92
69
93
- /**
94
- * Configures the {@link Pageable} to be used as fallback in case no {@link PageableDefault} or
95
- * {@link PageableDefault} (the latter only supported in legacy mode) can be found at the method parameter to be
96
- * resolved.
97
- * <p>
98
- * If you set this to {@literal Optional#empty()}, be aware that you controller methods will get {@literal null}
99
- * handed into them in case no {@link Pageable} data can be found in the request. Note, that doing so will require you
100
- * supply bot the page <em>and</em> the size parameter with the requests as there will be no default for any of the
101
- * parameters available.
102
- *
103
- * @param fallbackPageable the {@link Pageable} to be used as general fallback.
104
- */
105
- public void setFallbackPageable (Pageable fallbackPageable ) {
106
-
107
- Assert .notNull (fallbackPageable , "Fallback Pageable must not be null!" );
108
-
109
- this .fallbackPageable = fallbackPageable ;
110
- }
111
-
112
- /**
113
- * Returns whether the given {@link Pageable} is the fallback one.
114
- *
115
- * @param pageable can be {@literal null}.
116
- * @since 1.9
117
- * @return
118
- */
119
- public boolean isFallbackPageable (Pageable pageable ) {
120
- return fallbackPageable == null ? false : fallbackPageable .equals (pageable );
121
- }
122
-
123
- /**
124
- * Configures the maximum page size to be accepted. This allows to put an upper boundary of the page size to prevent
125
- * potential attacks trying to issue an {@link OutOfMemoryError}. Defaults to {@link #DEFAULT_MAX_PAGE_SIZE}.
126
- *
127
- * @param maxPageSize the maxPageSize to set
128
- */
129
- public void setMaxPageSize (int maxPageSize ) {
130
- this .maxPageSize = maxPageSize ;
131
- }
132
-
133
- /**
134
- * Retrieves the maximum page size to be accepted. This allows to put an upper boundary of the page size to prevent
135
- * potential attacks trying to issue an {@link OutOfMemoryError}. Defaults to {@link #DEFAULT_MAX_PAGE_SIZE}.
136
- *
137
- * @return the maximum page size allowed.
138
- */
139
- protected int getMaxPageSize () {
140
- return this .maxPageSize ;
141
- }
142
-
143
- /**
144
- * Configures the parameter name to be used to find the page number in the request. Defaults to {@code page}.
145
- *
146
- * @param pageParameterName the parameter name to be used, must not be {@literal null} or empty.
147
- */
148
- public void setPageParameterName (String pageParameterName ) {
149
-
150
- Assert .hasText (pageParameterName , "Page parameter name must not be null or empty!" );
151
- this .pageParameterName = pageParameterName ;
152
- }
153
-
154
- /**
155
- * Retrieves the parameter name to be used to find the page number in the request. Defaults to {@code page}.
156
- *
157
- * @return the parameter name to be used, never {@literal null} or empty.
158
- */
159
- protected String getPageParameterName () {
160
- return this .pageParameterName ;
161
- }
162
-
163
- /**
164
- * Configures the parameter name to be used to find the page size in the request. Defaults to {@code size}.
165
- *
166
- * @param sizeParameterName the parameter name to be used, must not be {@literal null} or empty.
167
- */
168
- public void setSizeParameterName (String sizeParameterName ) {
169
-
170
- Assert .hasText (sizeParameterName , "Size parameter name must not be null or empty!" );
171
- this .sizeParameterName = sizeParameterName ;
172
- }
173
-
174
- /**
175
- * Retrieves the parameter name to be used to find the page size in the request. Defaults to {@code size}.
176
- *
177
- * @return the parameter name to be used, never {@literal null} or empty.
178
- */
179
- protected String getSizeParameterName () {
180
- return this .sizeParameterName ;
181
- }
182
-
183
- /**
184
- * Configures a general prefix to be prepended to the page number and page size parameters. Useful to namespace the
185
- * property names used in case they are clashing with ones used by your application. By default, no prefix is used.
186
- *
187
- * @param prefix the prefix to be used or {@literal null} to reset to the default.
188
- */
189
- public void setPrefix (String prefix ) {
190
- this .prefix = prefix == null ? DEFAULT_PREFIX : prefix ;
191
- }
192
-
193
- /**
194
- * The delimiter to be used between the qualifier and the actual page number and size properties. Defaults to
195
- * {@code _}. So a qualifier of {@code foo} will result in a page number parameter of {@code foo_page}.
196
- *
197
- * @param qualifierDelimiter the delimiter to be used or {@literal null} to reset to the default.
198
- */
199
- public void setQualifierDelimiter (String qualifierDelimiter ) {
200
- this .qualifierDelimiter = qualifierDelimiter == null ? DEFAULT_QUALIFIER_DELIMITER : qualifierDelimiter ;
201
- }
202
-
203
- /**
204
- * Configures whether to expose and assume 1-based page number indexes in the request parameters. Defaults to
205
- * {@literal false}, meaning a page number of 0 in the request equals the first page. If this is set to
206
- * {@literal true}, a page number of 1 in the request will be considered the first page.
207
- *
208
- * @param oneIndexedParameters the oneIndexedParameters to set
209
- */
210
- public void setOneIndexedParameters (boolean oneIndexedParameters ) {
211
- this .oneIndexedParameters = oneIndexedParameters ;
212
- }
213
-
214
- /**
215
- * Indicates whether to expose and assume 1-based page number indexes in the request parameters. Defaults to
216
- * {@literal false}, meaning a page number of 0 in the request equals the first page. If this is set to
217
- * {@literal true}, a page number of 1 in the request will be considered the first page.
218
- *
219
- * @return whether to assume 1-based page number indexes in the request parameters.
220
- */
221
- protected boolean isOneIndexedParameters () {
222
- return this .oneIndexedParameters ;
223
- }
224
-
225
- /*
70
+ /*
226
71
* (non-Javadoc)
227
72
* @see org.springframework.web.method.support.HandlerMethodArgumentResolver#supportsParameter(org.springframework.core.MethodParameter)
228
73
*/
@@ -239,106 +84,17 @@ public boolean supportsParameter(MethodParameter parameter) {
239
84
public Pageable resolveArgument (MethodParameter methodParameter , @ Nullable ModelAndViewContainer mavContainer ,
240
85
NativeWebRequest webRequest , @ Nullable WebDataBinderFactory binderFactory ) {
241
86
242
- assertPageableUniqueness (methodParameter );
87
+ String page = webRequest .getParameter (getParameterNameToUse (getPageParameterName (), methodParameter ));
88
+ String pageSize = webRequest .getParameter (getParameterNameToUse (getSizeParameterName (), methodParameter ));
243
89
244
- Optional <Pageable > defaultOrFallback = getDefaultFromAnnotationOrFallback (methodParameter ).toOptional ();
245
-
246
- String pageString = webRequest .getParameter (getParameterNameToUse (pageParameterName , methodParameter ));
247
- String pageSizeString = webRequest .getParameter (getParameterNameToUse (sizeParameterName , methodParameter ));
248
-
249
- Optional <Integer > page = parseAndApplyBoundaries (pageString , Integer .MAX_VALUE , true );
250
- Optional <Integer > pageSize = parseAndApplyBoundaries (pageSizeString , maxPageSize , false );
251
-
252
- if (!(page .isPresent () && pageSize .isPresent ()) && !defaultOrFallback .isPresent ()) {
253
- return Pageable .unpaged ();
254
- }
255
-
256
- int p = page
257
- .orElseGet (() -> defaultOrFallback .map (Pageable ::getPageNumber ).orElseThrow (IllegalStateException ::new ));
258
- int ps = pageSize
259
- .orElseGet (() -> defaultOrFallback .map (Pageable ::getPageSize ).orElseThrow (IllegalStateException ::new ));
260
-
261
- // Limit lower bound
262
- ps = ps < 1 ? defaultOrFallback .map (Pageable ::getPageSize ).orElseThrow (IllegalStateException ::new ) : ps ;
263
- // Limit upper bound
264
- ps = ps > maxPageSize ? maxPageSize : ps ;
265
90
266
91
Sort sort = sortResolver .resolveArgument (methodParameter , mavContainer , webRequest , binderFactory );
92
+ Pageable pageable = getPageable (methodParameter , page , pageSize );
267
93
268
- return PageRequest .of (p , ps ,
269
- sort .isSorted () ? sort : defaultOrFallback .map (Pageable ::getSort ).orElseGet (Sort ::unsorted ));
270
- }
271
-
272
- /**
273
- * Returns the name of the request parameter to find the {@link Pageable} information in. Inspects the given
274
- * {@link MethodParameter} for {@link Qualifier} present and prefixes the given source parameter name with it.
275
- *
276
- * @param source the basic parameter name.
277
- * @param parameter the {@link MethodParameter} potentially qualified.
278
- * @return the name of the request parameter.
279
- */
280
- protected String getParameterNameToUse (String source , @ Nullable MethodParameter parameter ) {
281
-
282
- StringBuilder builder = new StringBuilder (prefix );
283
-
284
- Qualifier qualifier = parameter == null ? null : parameter .getParameterAnnotation (Qualifier .class );
285
-
286
- if (qualifier != null ) {
287
- builder .append (qualifier .value ());
288
- builder .append (qualifierDelimiter );
289
- }
290
-
291
- return builder .append (source ).toString ();
292
- }
293
-
294
- private Pageable getDefaultFromAnnotationOrFallback (MethodParameter methodParameter ) {
295
-
296
- PageableDefault defaults = methodParameter .getParameterAnnotation (PageableDefault .class );
297
-
298
- if (defaults != null ) {
299
- return getDefaultPageRequestFrom (methodParameter , defaults );
94
+ if (sort .isSorted ()) {
95
+ return PageRequest .of (pageable .getPageNumber (), pageable .getPageSize (), sort );
300
96
}
301
97
302
- return fallbackPageable ;
303
- }
304
-
305
- private static Pageable getDefaultPageRequestFrom (MethodParameter parameter , PageableDefault defaults ) {
306
-
307
- Integer defaultPageNumber = defaults .page ();
308
- Integer defaultPageSize = getSpecificPropertyOrDefaultFromValue (defaults , "size" );
309
-
310
- if (defaultPageSize < 1 ) {
311
- Method annotatedMethod = parameter .getMethod ();
312
- throw new IllegalStateException (String .format (INVALID_DEFAULT_PAGE_SIZE , annotatedMethod ));
313
- }
314
-
315
- if (defaults .sort ().length == 0 ) {
316
- return PageRequest .of (defaultPageNumber , defaultPageSize );
317
- }
318
-
319
- return PageRequest .of (defaultPageNumber , defaultPageSize , defaults .direction (), defaults .sort ());
320
- }
321
-
322
- /**
323
- * Tries to parse the given {@link String} into an integer and applies the given boundaries. Will return 0 if the
324
- * {@link String} cannot be parsed.
325
- *
326
- * @param parameter the parameter value.
327
- * @param upper the upper bound to be applied.
328
- * @param shiftIndex whether to shift the index if {@link #oneIndexedParameters} is set to true.
329
- * @return
330
- */
331
- private Optional <Integer > parseAndApplyBoundaries (@ Nullable String parameter , int upper , boolean shiftIndex ) {
332
-
333
- if (!StringUtils .hasText (parameter )) {
334
- return Optional .empty ();
335
- }
336
-
337
- try {
338
- int parsed = Integer .parseInt (parameter ) - (oneIndexedParameters && shiftIndex ? 1 : 0 );
339
- return Optional .of (parsed < 0 ? 0 : parsed > upper ? upper : parsed );
340
- } catch (NumberFormatException e ) {
341
- return Optional .of (0 );
342
- }
98
+ return pageable ;
343
99
}
344
100
}
0 commit comments