赞
踩
本篇介绍下Controller参数注解@RequestParam的使用方法,使用时的注意事项,以及与HttpServletRequest#getParameter方法的区别;
(1)是SpringMVC中接收普通参数的注解,注解打在Controller的方法参数上;
(2)可以将URL请求参数映射绑定到Controller的方法参数上,便于业务接收处理HTTP请求参数;
(3)通过注解的属性,可以对Controller参数做一些简单的"必传校验"和"默认值填充";
参考:org.springframework.web.bind.annotation.RequestParam
- @Target(ElementType.PARAMETER)
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface RequestParam {
-
- /**
- * Alias for {@link #name}.
- */
- @AliasFor("name")
- String value() default "";
-
- /**
- * The name of the request parameter to bind to.
- * @since 4.2
- */
- @AliasFor("value")
- String name() default "";
-
- /**
- * Whether the parameter is required.
- * <p>Defaults to {@code true}, leading to an exception being thrown
- * if the parameter is missing in the request. Switch this to
- * {@code false} if you prefer a {@code null} value if the parameter is
- * not present in the request.
- * <p>Alternatively, provide a {@link #defaultValue}, which implicitly
- * sets this flag to {@code false}.
- */
- boolean required() default true;
-
- /**
- * The default value to use as a fallback when the request parameter is
- * not provided or has an empty value.
- * <p>Supplying a default value implicitly sets {@link #required} to
- * {@code false}.
- */
- String defaultValue() default ValueConstants.DEFAULT_NONE;
-
- }
解释:
(1)name属性
- @RequestMapping(value = "/testParams", method = RequestMethod.POST)
- public BaseResponse<String> testParams(HttpServletRequest request, @RequestParam(name = "pkgName") String packageName) {
- log.warn("[request.getParameterMap={} request.getParameter(\"packageName\")={} packageName={}]",
- JSON.toJSONString(request.getParameterMap()),
- request.getParameter("packageName"),
- packageName);
- return BaseResponse.success("ok");
- }
控制台输出:
[request.getParameterMap={"pkgName":["com.ee.test.aaa"]} request.getParameter("pkgName")=com.ee.test.aaa packageName=com.ee.test.aaa]
URL中的参数名为"pkgName",通过注解name属性,Controller方法参数使用"packageName"去接收参数;
(2)required属性
默认require=true,不传递该参数则会报错;
控制台MissingServletRequestParameterException异常:
org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'pkgName' is not present
require=false,可以不用传递该参数;
- @RequestMapping(value = "/testParams", method = RequestMethod.POST)
- public BaseResponse<String> testParams(HttpServletRequest request, @RequestParam(name = "pkgName", required = false) String packageName) {
- log.warn("[request.getParameterMap={} request.getParameter(\"pkgName\")={} packageName={}]",
- JSON.toJSONString(request.getParameterMap()),
- request.getParameter("pkgName"),
- packageName);
- return BaseResponse.success("ok");
- }
(3)defaultValue属性
如果设置了该值,属性required=true将失效,URL未传该参数时,会使用默认值;
- @RequestMapping(value = "/testParams", method = RequestMethod.POST)
- public BaseResponse<String> testParams(HttpServletRequest request,
- @RequestParam(name = "pkgName", defaultValue = "defaultPkgName") String packageName) {
- log.warn("[request.getParameterMap={} request.getParameter(\"pkgName\")={} packageName={}]",
- JSON.toJSONString(request.getParameterMap()),
- request.getParameter("pkgName"),
- packageName);
- return BaseResponse.success("ok");
- }
控制台输出:
[request.getParameterMap={} request.getParameter("pkgName")=null packageName=defaultPkgName]
类型转换,这里defaultValue为String类型,参数类型为Integer,将defaultValue置位100,可以看到简单类型可以自动转换:
- @RequestMapping(value = "/testParams1", method = RequestMethod.POST)
- public BaseResponse<String> testParams1(HttpServletRequest request,
- @RequestParam(name = "appId", defaultValue = "100") Integer id) {
- log.warn("[request.getParameterMap={} request.getParameter(\"appId\")={} id={}]",
- JSON.toJSONString(request.getParameterMap()),
- request.getParameter("appId"),
- id);
- return BaseResponse.success("ok");
- }
[request.getParameterMap={} request.getParameter("appId")=null id=100]
如果Controller参数类型为Integer而默认值不是数值类型对应的字符串,请求时会报错方法参数错误:
- @RequestMapping(value = "/testParams2", method = RequestMethod.POST)
- public BaseResponse<String> testParams2(HttpServletRequest request,
- @RequestParam(name = "appId", defaultValue = "defaultAppId") Integer id) {
- log.warn("[request.getParameterMap={} request.getParameter(\"appId\")={} id={}]",
- JSON.toJSONString(request.getParameterMap()),
- request.getParameter("appId"),
- id);
- return BaseResponse.success("ok");
- }
MethodArgumentTypeMismatchException, bad param type: bad params type: id required type java.lang.Integer
不使用@RequestParam注解,URL参数依然可以接收到;
- // 不加注解接收参数
- @RequestMapping("/test1")
- public String test1(int userId) {
- return "list";
- }
-
- // 加注解接收参数
- @RequestMapping("/test2")
- public String test2(@RequestParam int userId) {
- return "list";
- }
表现为:
(1)不加@RequestParam时,请求的参数名需要和Controller的方法参数名保持一致才能生效;使用@RequestParam时,可以通过@RequestParam(“userId”)或者@RequestParam(value = “userId”)指定传入的参数名;
(2)不加@RequestParam时,参数为非必传,加@RequestParam而不指定注解属性require=false时,参数为必传;并且可以通过@RequestParam(defaultValue = “0”)指定参数默认值;
request.getParameter("name")和@RequestParam String name的获取方式是不同的,对比如下:
HttpServletRequest#getParameter
(1) request.getParameter(String name):获取name对应的value,如果有多个,返回第一个;
(2) request.getParameterNames():获取request里所有的name,返回Enumeration类型;
(3) request.getParameterValues(String name):获取name对应的所有value;
其实这三种方式都是从一个全局变量Map<String, String[]> 中获取的值,由此可见:key是String类型,也就是name,而value是String数组类型,请求时是可以提供多个相同的key,value值进行请求,下面是个示例;
请求参数相同参数名,传入两个不同的值:
控制台结果:
[request.getParameterMap={"pkgName":["AAA","BBB"]} request.getParameter("pkgName")=AAA packageName=AAA,BBB]
可见:
从上面的结果来看,至少可以知道,@RequestParam与request.getParameter的逻辑是不一样的,来断点看一下:
答案找到了,在这里完成字符串的拼接;
从源码来看,@RequestParam与request.getParameter的逻辑是不一样的,实际调用的是getParameterValues方法,该方法返回的是String数组,并且源码中做了判断,在数组不为null的情况下,如果数组长度为1返回第一个元素,如果否则(大于1)返回当前数组,并通过Spring包的StringUtils将数组转成字符串,用逗号拼接;
结论:request.getParameter和@RequestParam本质上是不同的,所以在不确定是否有多个value的情况下,@RequestParam是不能代替request.getParameter来使用的!
规范的使用@RequestParam写法应该是@RequestParam String[] packageNames,如下:
而request.getParameter获取的确确实实是第一个value,可能我们研发过程中很少遇到多个相同key提交请求这种情况,但是这种情况是真实存在的,如果不了解这段代码,还是把@RequestParam当作获取第一个参数使用,早晚会出问题!
参考:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。