浅谈SpringMVC接收参数

143 阅读6分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

最近接到了一个新的项目,修改某些接口的时候发现Controller层有趣的地方,所以今天谈一谈SpringMVC接收参数的应用。

一、第一种接收参数的方式通过HttpServletRequest来接收参数

接口:

@PostMapping("/test2")
public void test2(HttpServletRequest request){
    String name = request.getParameter("name");
    String age = request.getParameter("age");
    String equipment = request.getParameter("equipment");
    
    System.out.println(name);
    System.out.println(age);
    System.out.println(equipment);
}

postman调用接口:

image.png

控制台输出:

image.png

相信很多人都看出问题来了,用request接收前端传过来的参数非常方便,因为只要一个HttpServletRequest request就可以接收一切;但是使用起来却不方便,request.getParameter()返回的是一个String类型,也就是说不是String类型的参数需要手动强转,比如上面的age、equipment

这几个注解都是lombok插件自带的,@Data提供getter、setter等方法;@AllArgsConstructor提供有参构造方法、@NoArgsConstructor是提供无参构造、@ToString是提供toString方法,在这里只是想偷个懒

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
class Equipment{
    // 武器
    public String weapon;
    // 坐骑
    public String mount;
}

当然了,现在开源封装工具蛮多的,用起来也算方便

接口:

@PostMapping("/test2")
public void test2(HttpServletRequest request){
    // 字符串数据
    String name = request.getParameter("name");
    // int类型数据
    String age = request.getParameter("age");

    // 数组数据
    String subordinate = request.getParameter("subordinate");
    String[] subordinateArr = subordinate.split(",");

    // 对象数据
    String equipment = request.getParameter("equipment");
    Equipment equipmentBean = JSONUtil.toBean(equipment, Equipment.class);

    System.out.println("=======================");
    System.out.println(name);
    System.out.println(Integer.parseInt(age));
    System.out.println("=======================");
    System.out.println(subordinate);
    System.out.println(subordinateArr[2]);
    for (String s : subordinateArr) {
        System.out.println(s);
    }
    System.out.println("=======================");
    System.out.println(equipment);
    System.out.println(equipmentBean);
}

postman调用接口:

image.png

控制台输出: image.png

用request接收参数跟map接收参数,一般是不推荐的,这两种方式虽然使用起来方便,但是在方法调用之间我们是没办法看到他们里面有哪些属性,只能通过debug方式或者直接输出到控制台查看,那么如果我们如果将request.getParameter("")、map.get("")的key,也就是字符串写错,比如仅仅写错一个字母,都有可能让我们调试半天,比如Object跟0bject,我们总不能保证自己从来不会手抖吧。

二、第二种接收参数的方式在Controller的方法上直接写参数,这种方式比较直观

(1)我们知道int、float等基本类型默认值不是null,他们的包装类默认值是null,那么接收参数时就要注意了

controller使用int类型接收age参数,即使我只是接收并没有使用

@PostMapping("/test3")
public void test3(String name, int age){
    System.out.println(name);
}

postman没有传age参数

image.png

控制台依然会报错

java.lang.IllegalStateException: Optional int parameter 'age' is present but cannot be translated into a null value due to being declared as a primitive type. Consider declaring it as object wrapper for the corresponding primitive type.

也就说age在这里是必传的

image.png

image.png

甚至加上注解标记为非必传参数

@PostMapping("/test3")
public void test3(String name, @RequestParam(name = "age", required = false) int age){
    System.out.println(name);
}

但还是会报错: image.png

那么解决办法是什么呢?只要把相应的基本类型换成包装类就可以了

@PostMapping("/test3")
public void test3(String name, Integer age){
    System.out.println(name);
    System.out.println(age);
}

当然了,没传值的话,age会接收到null

image.png

image.png

(2)还有一个点就是,这种方式没办法直接接收一个List列表,所以我们得用数组接收,如果需要用列表的话再进行转换。

用List接收:

@PostMapping("/test3")
public void test3(String name, Integer age, List<String> subordinateArr){
    System.out.println(name);
    System.out.println(age);
    System.out.println("===================");
    for (String s : subordinateArr) {
        System.out.println(s);
    }
}

postman调用接口:

image.png

报错:

image.png

用List接收也可以:下面 @RequestParam 时会说

用数组接收:

@PostMapping("/test3")
public void test3(String name, Integer age, String[] subordinateArr){
    System.out.println(name);
    System.out.println(age);
    System.out.println("===================");
    for (String s : subordinateArr) {
        System.out.println(s);
    }
}

正常:

image.png

三、直接用Bean对象接收。上面方式只适合接收参数少的情况,那么参数多的时候我们会选择用一个bean对象接收,一般用request作后缀,主要是跟xxxPojo、xxxVo、xxxConvert等bean对象区分开来,这算是一种编码命名规范 接口:

    @PostMapping("/test3")
    public void test3(String name, Integer age, String[] subordinateArr, EquipmentRequest equipmentRequest){
        System.out.println(name);
        System.out.println(age);
        System.out.println("===================");
        for (String s : subordinateArr) {
            System.out.println(s);
        }
        System.out.println("===================");
        System.out.println(equipmentRequest);
    }
}

postman调用接口:

image.png

控制台输出: image.png

很明显可以看到EquipmentRequest没有接收到参数,那是因为前端/postman传参的时候直接将EquipmentRequest传过来了,这种情况很少见……因为正常的前端都会将属性一个个传过来,如下:

image.png

正常接收了

image.png

四、这里我们说一下参数注解的区别

(1)首先说一下 @RequestParam,

image.png

可以看到有四个属性

image.png

可以看到name跟value的别名是相互的,也就是这两个注解没啥区别,都是指明参数的

举例子说明:

@RequestParam List subordinateArr没有指定name或者value,那么前端传参数用subordinateArr

@RequestParam(name = "aaa") List subordinateArr制定了name="aaa",那么前端传参数就得用aaa,如果不传aaa就会报错,这就跟第三个属性required有关系了

如果加上了@RequestParam 那么required默认为true,也就是必传的意思,不传就会报错:Required String parameter 'aaa' is not present

第四个属性 defaultValue 顾名思义,默认值,加上了这个属性等于将required设置为false,前端对这个属性值可以不传,不传的话这个属性值就被设置成defaultValue

@PostMapping("/test3")
public void test3( @RequestParam(name = "name", defaultValue = "的点点滴滴") String name, Integer age){
    System.out.println(name);
    System.out.println(age);
    System.out.println("===================");

}

image.png

image.png

那么普通参数用不用@RequestParam这个注解其实问题都不大,问题是在如果参数用List接收的时候,用@RequestParam有什么区别?

同样的Postman传参

image.png

不加@RequestParam

image.png

加@RequestParam

image.png

image.png

在这里我上网查,得到的结论是:

不加注解只能接收url参数;

加了@RequestParam后即能接收url的参数,也能接收form-data参数,但是只支持Content-Type: 为 application/x-www-form-urlencoded的参数传入

但是由始至终我一直都在用form-data传参,但是参数一直没有用@RequestParam注解,还是能接收到,除了List接收时出现了问题(加了@RequestParam就能从form-data接收参数了),暂时没有深究了

而且到这里我才发现,无论后端用数组接收还是List接收,都是英文逗号分割的参数

如:{123,234,345},那么分割后就是{123 234 345}三个

如:[123,234,345],那么分割后就是[123 234 345]三个

如:123,234,345,那么分割后就是123 234 345三个

也就是说最外面不能用{}或者其他的东西包裹原始数据,原始数据就用英文逗号隔开就好

(2) @requestBody与@requestParam的区别

spring的RequestParam注解接收的参数是来自于requestHeader中,即请求头。都是用来获取请求路径url 中的动态参数。也就是在url中,格式为xxx?username=123&password=456,也即是key=value的形式。

RequestBody注解接收的参数则是来自于requestBody中,即请求体中。

直接用@requestBody

image.png

image.png

报错:multipart/form-data……不支持

那么换成Json方式传参

image.png

image.png

而且@RequestBody参数只支持传入一个对象参数,它实际上是将输入流的body体作为一个整体进行转换,而body整体只有一份,解析完成之后会关闭输入流。如果传入多个参数则会报错。除非自定义参数解析器。

比如定义了两个@RequestBody就会报错

image.png

image.png

而如果定义了一个@RequestBody,但却接收两个参数,那么没有定义@RequestBody的参数将接收到为null

image.png