代码技巧——Controller参数注解@RequestParam

本篇介绍下Controller参数注解@RequestParam的使用方法,使用时的注意事项,以及与HttpServletRequest#getParameter方法的区别;
 

1. 注解@RequestParam的作用

(1)是SpringMVC中接收普通参数的注解,注解打在Controller的方法参数上;

(2)可以将URL请求参数映射绑定到Controller的方法参数上,便于业务接收处理HTTP请求参数;

(3)通过注解的属性,可以对Controller参数做一些简单的"必传校验"和"默认值填充";

2. 注解@RequestParam参数说明

参考: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;

}

解释:

  • value:name属性的别名,作用相同;
  • name:URL中的参数名,映射到当前注解对应的参数上;如果没有设置该属性,方法参数名则和接口参数名匹配;
  • required:默认值是"true",表示该请求路径中必须包含该参数,如果不包含就会报404错误码;
  • defaultValue:String类型,默认参数值,如果设置了该值,属性required=true将失效,自动为false;如果没有传该参数,就使用默认值;默认值也可以是SpEL表达式

3. 使用示例

(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

4. 也可以不使用@RequestParam注解——注解加与不加的区别

不使用@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”)指定参数默认值;

5. 注解@RequestParam与HttpServletRequest#getParameter方法的区别

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]

可见:

  • Map中是全部的参数K/V,V为数组类型,当相同参数传递多个值时,加入数组;
  • request.getParameter会取数组中的第一个值,这里就是AAA;
  • Controller方法参数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当作获取第一个参数使用,早晚会出问题!

参考:

@RequestParam详解

request.getParameter()和@RequestParam的区别

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值