001/*
002 * Copyright 2002-2017 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.bind;
018
019import javax.servlet.ServletRequest;
020
021import org.springframework.lang.Nullable;
022
023/**
024 * Parameter extraction methods, for an approach distinct from data binding,
025 * in which parameters of specific types are required.
026 *
027 * <p>This approach is very useful for simple submissions, where binding
028 * request parameters to a command object would be overkill.
029 *
030 * @author Juergen Hoeller
031 * @author Keith Donald
032 * @since 2.0
033 */
034public abstract class ServletRequestUtils {
035
036        private static final IntParser INT_PARSER = new IntParser();
037
038        private static final LongParser LONG_PARSER = new LongParser();
039
040        private static final FloatParser FLOAT_PARSER = new FloatParser();
041
042        private static final DoubleParser DOUBLE_PARSER = new DoubleParser();
043
044        private static final BooleanParser BOOLEAN_PARSER = new BooleanParser();
045
046        private static final StringParser STRING_PARSER = new StringParser();
047
048
049        /**
050         * Get an Integer parameter, or {@code null} if not present.
051         * Throws an exception if it the parameter value isn't a number.
052         * @param request current HTTP request
053         * @param name the name of the parameter
054         * @return the Integer value, or {@code null} if not present
055         * @throws ServletRequestBindingException a subclass of ServletException,
056         * so it doesn't need to be caught
057         */
058        @Nullable
059        public static Integer getIntParameter(ServletRequest request, String name)
060                        throws ServletRequestBindingException {
061
062                if (request.getParameter(name) == null) {
063                        return null;
064                }
065                return getRequiredIntParameter(request, name);
066        }
067
068        /**
069         * Get an int parameter, with a fallback value. Never throws an exception.
070         * Can pass a distinguished value as default to enable checks of whether it was supplied.
071         * @param request current HTTP request
072         * @param name the name of the parameter
073         * @param defaultVal the default value to use as fallback
074         */
075        public static int getIntParameter(ServletRequest request, String name, int defaultVal) {
076                if (request.getParameter(name) == null) {
077                        return defaultVal;
078                }
079                try {
080                        return getRequiredIntParameter(request, name);
081                }
082                catch (ServletRequestBindingException ex) {
083                        return defaultVal;
084                }
085        }
086
087        /**
088         * Get an array of int parameters, return an empty array if not found.
089         * @param request current HTTP request
090         * @param name the name of the parameter with multiple possible values
091         */
092        public static int[] getIntParameters(ServletRequest request, String name) {
093                try {
094                        return getRequiredIntParameters(request, name);
095                }
096                catch (ServletRequestBindingException ex) {
097                        return new int[0];
098                }
099        }
100
101        /**
102         * Get an int parameter, throwing an exception if it isn't found or isn't a number.
103         * @param request current HTTP request
104         * @param name the name of the parameter
105         * @throws ServletRequestBindingException a subclass of ServletException,
106         * so it doesn't need to be caught
107         */
108        public static int getRequiredIntParameter(ServletRequest request, String name)
109                        throws ServletRequestBindingException {
110
111                return INT_PARSER.parseInt(name, request.getParameter(name));
112        }
113
114        /**
115         * Get an array of int parameters, throwing an exception if not found or one is not a number..
116         * @param request current HTTP request
117         * @param name the name of the parameter with multiple possible values
118         * @throws ServletRequestBindingException a subclass of ServletException,
119         * so it doesn't need to be caught
120         */
121        public static int[] getRequiredIntParameters(ServletRequest request, String name)
122                        throws ServletRequestBindingException {
123
124                return INT_PARSER.parseInts(name, request.getParameterValues(name));
125        }
126
127
128        /**
129         * Get a Long parameter, or {@code null} if not present.
130         * Throws an exception if it the parameter value isn't a number.
131         * @param request current HTTP request
132         * @param name the name of the parameter
133         * @return the Long value, or {@code null} if not present
134         * @throws ServletRequestBindingException a subclass of ServletException,
135         * so it doesn't need to be caught
136         */
137        @Nullable
138        public static Long getLongParameter(ServletRequest request, String name)
139                        throws ServletRequestBindingException {
140
141                if (request.getParameter(name) == null) {
142                        return null;
143                }
144                return getRequiredLongParameter(request, name);
145        }
146
147        /**
148         * Get a long parameter, with a fallback value. Never throws an exception.
149         * Can pass a distinguished value as default to enable checks of whether it was supplied.
150         * @param request current HTTP request
151         * @param name the name of the parameter
152         * @param defaultVal the default value to use as fallback
153         */
154        public static long getLongParameter(ServletRequest request, String name, long defaultVal) {
155                if (request.getParameter(name) == null) {
156                        return defaultVal;
157                }
158                try {
159                        return getRequiredLongParameter(request, name);
160                }
161                catch (ServletRequestBindingException ex) {
162                        return defaultVal;
163                }
164        }
165
166        /**
167         * Get an array of long parameters, return an empty array if not found.
168         * @param request current HTTP request
169         * @param name the name of the parameter with multiple possible values
170         */
171        public static long[] getLongParameters(ServletRequest request, String name) {
172                try {
173                        return getRequiredLongParameters(request, name);
174                }
175                catch (ServletRequestBindingException ex) {
176                        return new long[0];
177                }
178        }
179
180        /**
181         * Get a long parameter, throwing an exception if it isn't found or isn't a number.
182         * @param request current HTTP request
183         * @param name the name of the parameter
184         * @throws ServletRequestBindingException a subclass of ServletException,
185         * so it doesn't need to be caught
186         */
187        public static long getRequiredLongParameter(ServletRequest request, String name)
188                        throws ServletRequestBindingException {
189
190                return LONG_PARSER.parseLong(name, request.getParameter(name));
191        }
192
193        /**
194         * Get an array of long parameters, throwing an exception if not found or one is not a number.
195         * @param request current HTTP request
196         * @param name the name of the parameter with multiple possible values
197         * @throws ServletRequestBindingException a subclass of ServletException,
198         * so it doesn't need to be caught
199         */
200        public static long[] getRequiredLongParameters(ServletRequest request, String name)
201                        throws ServletRequestBindingException {
202
203                return LONG_PARSER.parseLongs(name, request.getParameterValues(name));
204        }
205
206
207        /**
208         * Get a Float parameter, or {@code null} if not present.
209         * Throws an exception if it the parameter value isn't a number.
210         * @param request current HTTP request
211         * @param name the name of the parameter
212         * @return the Float value, or {@code null} if not present
213         * @throws ServletRequestBindingException a subclass of ServletException,
214         * so it doesn't need to be caught
215         */
216        @Nullable
217        public static Float getFloatParameter(ServletRequest request, String name)
218                        throws ServletRequestBindingException {
219
220                if (request.getParameter(name) == null) {
221                        return null;
222                }
223                return getRequiredFloatParameter(request, name);
224        }
225
226        /**
227         * Get a float parameter, with a fallback value. Never throws an exception.
228         * Can pass a distinguished value as default to enable checks of whether it was supplied.
229         * @param request current HTTP request
230         * @param name the name of the parameter
231         * @param defaultVal the default value to use as fallback
232         */
233        public static float getFloatParameter(ServletRequest request, String name, float defaultVal) {
234                if (request.getParameter(name) == null) {
235                        return defaultVal;
236                }
237                try {
238                        return getRequiredFloatParameter(request, name);
239                }
240                catch (ServletRequestBindingException ex) {
241                        return defaultVal;
242                }
243        }
244
245        /**
246         * Get an array of float parameters, return an empty array if not found.
247         * @param request current HTTP request
248         * @param name the name of the parameter with multiple possible values
249         */
250        public static float[] getFloatParameters(ServletRequest request, String name) {
251                try {
252                        return getRequiredFloatParameters(request, name);
253                }
254                catch (ServletRequestBindingException ex) {
255                        return new float[0];
256                }
257        }
258
259        /**
260         * Get a float parameter, throwing an exception if it isn't found or isn't a number.
261         * @param request current HTTP request
262         * @param name the name of the parameter
263         * @throws ServletRequestBindingException a subclass of ServletException,
264         * so it doesn't need to be caught
265         */
266        public static float getRequiredFloatParameter(ServletRequest request, String name)
267                        throws ServletRequestBindingException {
268
269                return FLOAT_PARSER.parseFloat(name, request.getParameter(name));
270        }
271
272        /**
273         * Get an array of float parameters, throwing an exception if not found or one is not a number.
274         * @param request current HTTP request
275         * @param name the name of the parameter with multiple possible values
276         * @throws ServletRequestBindingException a subclass of ServletException,
277         * so it doesn't need to be caught
278         */
279        public static float[] getRequiredFloatParameters(ServletRequest request, String name)
280                        throws ServletRequestBindingException {
281
282                return FLOAT_PARSER.parseFloats(name, request.getParameterValues(name));
283        }
284
285
286        /**
287         * Get a Double parameter, or {@code null} if not present.
288         * Throws an exception if it the parameter value isn't a number.
289         * @param request current HTTP request
290         * @param name the name of the parameter
291         * @return the Double value, or {@code null} if not present
292         * @throws ServletRequestBindingException a subclass of ServletException,
293         * so it doesn't need to be caught
294         */
295        @Nullable
296        public static Double getDoubleParameter(ServletRequest request, String name)
297                        throws ServletRequestBindingException {
298
299                if (request.getParameter(name) == null) {
300                        return null;
301                }
302                return getRequiredDoubleParameter(request, name);
303        }
304
305        /**
306         * Get a double parameter, with a fallback value. Never throws an exception.
307         * Can pass a distinguished value as default to enable checks of whether it was supplied.
308         * @param request current HTTP request
309         * @param name the name of the parameter
310         * @param defaultVal the default value to use as fallback
311         */
312        public static double getDoubleParameter(ServletRequest request, String name, double defaultVal) {
313                if (request.getParameter(name) == null) {
314                        return defaultVal;
315                }
316                try {
317                        return getRequiredDoubleParameter(request, name);
318                }
319                catch (ServletRequestBindingException ex) {
320                        return defaultVal;
321                }
322        }
323
324        /**
325         * Get an array of double parameters, return an empty array if not found.
326         * @param request current HTTP request
327         * @param name the name of the parameter with multiple possible values
328         */
329        public static double[] getDoubleParameters(ServletRequest request, String name) {
330                try {
331                        return getRequiredDoubleParameters(request, name);
332                }
333                catch (ServletRequestBindingException ex) {
334                        return new double[0];
335                }
336        }
337
338        /**
339         * Get a double parameter, throwing an exception if it isn't found or isn't a number.
340         * @param request current HTTP request
341         * @param name the name of the parameter
342         * @throws ServletRequestBindingException a subclass of ServletException,
343         * so it doesn't need to be caught
344         */
345        public static double getRequiredDoubleParameter(ServletRequest request, String name)
346                        throws ServletRequestBindingException {
347
348                return DOUBLE_PARSER.parseDouble(name, request.getParameter(name));
349        }
350
351        /**
352         * Get an array of double parameters, throwing an exception if not found or one is not a number.
353         * @param request current HTTP request
354         * @param name the name of the parameter with multiple possible values
355         * @throws ServletRequestBindingException a subclass of ServletException,
356         * so it doesn't need to be caught
357         */
358        public static double[] getRequiredDoubleParameters(ServletRequest request, String name)
359                        throws ServletRequestBindingException {
360
361                return DOUBLE_PARSER.parseDoubles(name, request.getParameterValues(name));
362        }
363
364
365        /**
366         * Get a Boolean parameter, or {@code null} if not present.
367         * Throws an exception if it the parameter value isn't a boolean.
368         * <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
369         * treats every other non-empty value as false (i.e. parses leniently).
370         * @param request current HTTP request
371         * @param name the name of the parameter
372         * @return the Boolean value, or {@code null} if not present
373         * @throws ServletRequestBindingException a subclass of ServletException,
374         * so it doesn't need to be caught
375         */
376        @Nullable
377        public static Boolean getBooleanParameter(ServletRequest request, String name)
378                        throws ServletRequestBindingException {
379
380                if (request.getParameter(name) == null) {
381                        return null;
382                }
383                return (getRequiredBooleanParameter(request, name));
384        }
385
386        /**
387         * Get a boolean parameter, with a fallback value. Never throws an exception.
388         * Can pass a distinguished value as default to enable checks of whether it was supplied.
389         * <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
390         * treats every other non-empty value as false (i.e. parses leniently).
391         * @param request current HTTP request
392         * @param name the name of the parameter
393         * @param defaultVal the default value to use as fallback
394         */
395        public static boolean getBooleanParameter(ServletRequest request, String name, boolean defaultVal) {
396                if (request.getParameter(name) == null) {
397                        return defaultVal;
398                }
399                try {
400                        return getRequiredBooleanParameter(request, name);
401                }
402                catch (ServletRequestBindingException ex) {
403                        return defaultVal;
404                }
405        }
406
407        /**
408         * Get an array of boolean parameters, return an empty array if not found.
409         * <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
410         * treats every other non-empty value as false (i.e. parses leniently).
411         * @param request current HTTP request
412         * @param name the name of the parameter with multiple possible values
413         */
414        public static boolean[] getBooleanParameters(ServletRequest request, String name) {
415                try {
416                        return getRequiredBooleanParameters(request, name);
417                }
418                catch (ServletRequestBindingException ex) {
419                        return new boolean[0];
420                }
421        }
422
423        /**
424         * Get a boolean parameter, throwing an exception if it isn't found
425         * or isn't a boolean.
426         * <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
427         * treats every other non-empty value as false (i.e. parses leniently).
428         * @param request current HTTP request
429         * @param name the name of the parameter
430         * @throws ServletRequestBindingException a subclass of ServletException,
431         * so it doesn't need to be caught
432         */
433        public static boolean getRequiredBooleanParameter(ServletRequest request, String name)
434                        throws ServletRequestBindingException {
435
436                return BOOLEAN_PARSER.parseBoolean(name, request.getParameter(name));
437        }
438
439        /**
440         * Get an array of boolean parameters, throwing an exception if not found
441         * or one isn't a boolean.
442         * <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
443         * treats every other non-empty value as false (i.e. parses leniently).
444         * @param request current HTTP request
445         * @param name the name of the parameter
446         * @throws ServletRequestBindingException a subclass of ServletException,
447         * so it doesn't need to be caught
448         */
449        public static boolean[] getRequiredBooleanParameters(ServletRequest request, String name)
450                        throws ServletRequestBindingException {
451
452                return BOOLEAN_PARSER.parseBooleans(name, request.getParameterValues(name));
453        }
454
455
456        /**
457         * Get a String parameter, or {@code null} if not present.
458         * @param request current HTTP request
459         * @param name the name of the parameter
460         * @return the String value, or {@code null} if not present
461         * @throws ServletRequestBindingException a subclass of ServletException,
462         * so it doesn't need to be caught
463         */
464        @Nullable
465        public static String getStringParameter(ServletRequest request, String name)
466                        throws ServletRequestBindingException {
467
468                if (request.getParameter(name) == null) {
469                        return null;
470                }
471                return getRequiredStringParameter(request, name);
472        }
473
474        /**
475         * Get a String parameter, with a fallback value. Never throws an exception.
476         * Can pass a distinguished value to default to enable checks of whether it was supplied.
477         * @param request current HTTP request
478         * @param name the name of the parameter
479         * @param defaultVal the default value to use as fallback
480         */
481        public static String getStringParameter(ServletRequest request, String name, String defaultVal) {
482                String val = request.getParameter(name);
483                return (val != null ? val : defaultVal);
484        }
485
486        /**
487         * Get an array of String parameters, return an empty array if not found.
488         * @param request current HTTP request
489         * @param name the name of the parameter with multiple possible values
490         */
491        public static String[] getStringParameters(ServletRequest request, String name) {
492                try {
493                        return getRequiredStringParameters(request, name);
494                }
495                catch (ServletRequestBindingException ex) {
496                        return new String[0];
497                }
498        }
499
500        /**
501         * Get a String parameter, throwing an exception if it isn't found.
502         * @param request current HTTP request
503         * @param name the name of the parameter
504         * @throws ServletRequestBindingException a subclass of ServletException,
505         * so it doesn't need to be caught
506         */
507        public static String getRequiredStringParameter(ServletRequest request, String name)
508                        throws ServletRequestBindingException {
509
510                return STRING_PARSER.validateRequiredString(name, request.getParameter(name));
511        }
512
513        /**
514         * Get an array of String parameters, throwing an exception if not found.
515         * @param request current HTTP request
516         * @param name the name of the parameter
517         * @throws ServletRequestBindingException a subclass of ServletException,
518         * so it doesn't need to be caught
519         */
520        public static String[] getRequiredStringParameters(ServletRequest request, String name)
521                        throws ServletRequestBindingException {
522
523                return STRING_PARSER.validateRequiredStrings(name, request.getParameterValues(name));
524        }
525
526
527        private abstract static class ParameterParser<T> {
528
529                protected final T parse(String name, String parameter) throws ServletRequestBindingException {
530                        validateRequiredParameter(name, parameter);
531                        try {
532                                return doParse(parameter);
533                        }
534                        catch (NumberFormatException ex) {
535                                throw new ServletRequestBindingException(
536                                                "Required " + getType() + " parameter '" + name + "' with value of '" +
537                                                parameter + "' is not a valid number", ex);
538                        }
539                }
540
541                protected final void validateRequiredParameter(String name, @Nullable Object parameter)
542                                throws ServletRequestBindingException {
543
544                        if (parameter == null) {
545                                throw new MissingServletRequestParameterException(name, getType());
546                        }
547                }
548
549                protected abstract String getType();
550
551                protected abstract T doParse(String parameter) throws NumberFormatException;
552        }
553
554
555        private static class IntParser extends ParameterParser<Integer> {
556
557                @Override
558                protected String getType() {
559                        return "int";
560                }
561
562                @Override
563                protected Integer doParse(String s) throws NumberFormatException {
564                        return Integer.valueOf(s);
565                }
566
567                public int parseInt(String name, String parameter) throws ServletRequestBindingException {
568                        return parse(name, parameter);
569                }
570
571                public int[] parseInts(String name, String[] values) throws ServletRequestBindingException {
572                        validateRequiredParameter(name, values);
573                        int[] parameters = new int[values.length];
574                        for (int i = 0; i < values.length; i++) {
575                                parameters[i] = parseInt(name, values[i]);
576                        }
577                        return parameters;
578                }
579        }
580
581
582        private static class LongParser extends ParameterParser<Long> {
583
584                @Override
585                protected String getType() {
586                        return "long";
587                }
588
589                @Override
590                protected Long doParse(String parameter) throws NumberFormatException {
591                        return Long.valueOf(parameter);
592                }
593
594                public long parseLong(String name, String parameter) throws ServletRequestBindingException {
595                        return parse(name, parameter);
596                }
597
598                public long[] parseLongs(String name, String[] values) throws ServletRequestBindingException {
599                        validateRequiredParameter(name, values);
600                        long[] parameters = new long[values.length];
601                        for (int i = 0; i < values.length; i++) {
602                                parameters[i] = parseLong(name, values[i]);
603                        }
604                        return parameters;
605                }
606        }
607
608
609        private static class FloatParser extends ParameterParser<Float> {
610
611                @Override
612                protected String getType() {
613                        return "float";
614                }
615
616                @Override
617                protected Float doParse(String parameter) throws NumberFormatException {
618                        return Float.valueOf(parameter);
619                }
620
621                public float parseFloat(String name, String parameter) throws ServletRequestBindingException {
622                        return parse(name, parameter);
623                }
624
625                public float[] parseFloats(String name, String[] values) throws ServletRequestBindingException {
626                        validateRequiredParameter(name, values);
627                        float[] parameters = new float[values.length];
628                        for (int i = 0; i < values.length; i++) {
629                                parameters[i] = parseFloat(name, values[i]);
630                        }
631                        return parameters;
632                }
633        }
634
635
636        private static class DoubleParser extends ParameterParser<Double> {
637
638                @Override
639                protected String getType() {
640                        return "double";
641                }
642
643                @Override
644                protected Double doParse(String parameter) throws NumberFormatException {
645                        return Double.valueOf(parameter);
646                }
647
648                public double parseDouble(String name, String parameter) throws ServletRequestBindingException {
649                        return parse(name, parameter);
650                }
651
652                public double[] parseDoubles(String name, String[] values) throws ServletRequestBindingException {
653                        validateRequiredParameter(name, values);
654                        double[] parameters = new double[values.length];
655                        for (int i = 0; i < values.length; i++) {
656                                parameters[i] = parseDouble(name, values[i]);
657                        }
658                        return parameters;
659                }
660        }
661
662
663        private static class BooleanParser extends ParameterParser<Boolean> {
664
665                @Override
666                protected String getType() {
667                        return "boolean";
668                }
669
670                @Override
671                protected Boolean doParse(String parameter) throws NumberFormatException {
672                        return (parameter.equalsIgnoreCase("true") || parameter.equalsIgnoreCase("on") ||
673                                        parameter.equalsIgnoreCase("yes") || parameter.equals("1"));
674                }
675
676                public boolean parseBoolean(String name, String parameter) throws ServletRequestBindingException {
677                        return parse(name, parameter);
678                }
679
680                public boolean[] parseBooleans(String name, String[] values) throws ServletRequestBindingException {
681                        validateRequiredParameter(name, values);
682                        boolean[] parameters = new boolean[values.length];
683                        for (int i = 0; i < values.length; i++) {
684                                parameters[i] = parseBoolean(name, values[i]);
685                        }
686                        return parameters;
687                }
688        }
689
690
691        private static class StringParser extends ParameterParser<String> {
692
693                @Override
694                protected String getType() {
695                        return "string";
696                }
697
698                @Override
699                protected String doParse(String parameter) throws NumberFormatException {
700                        return parameter;
701                }
702
703                public String validateRequiredString(String name, String value) throws ServletRequestBindingException {
704                        validateRequiredParameter(name, value);
705                        return value;
706                }
707
708                public String[] validateRequiredStrings(String name, String[] values) throws ServletRequestBindingException {
709                        validateRequiredParameter(name, values);
710                        for (String value : values) {
711                                validateRequiredParameter(name, value);
712                        }
713                        return values;
714                }
715        }
716
717}