持续创作,加速成长!这是我参与「掘金日新计划 · 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调用接口:
控制台输出:
相信很多人都看出问题来了,用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调用接口:
控制台输出:
用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参数
控制台依然会报错
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在这里是必传的
甚至加上注解标记为非必传参数
@PostMapping("/test3")
public void test3(String name, @RequestParam(name = "age", required = false) int age){
System.out.println(name);
}
但还是会报错:
那么解决办法是什么呢?只要把相应的基本类型换成包装类就可以了
@PostMapping("/test3")
public void test3(String name, Integer age){
System.out.println(name);
System.out.println(age);
}
当然了,没传值的话,age会接收到null
(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调用接口:
报错:
用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);
}
}
正常:
三、直接用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调用接口:
控制台输出:
很明显可以看到EquipmentRequest没有接收到参数,那是因为前端/postman传参的时候直接将EquipmentRequest传过来了,这种情况很少见……因为正常的前端都会将属性一个个传过来,如下:
正常接收了
四、这里我们说一下参数注解的区别
(1)首先说一下 @RequestParam,
可以看到有四个属性
可以看到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("===================");
}
那么普通参数用不用@RequestParam这个注解其实问题都不大,问题是在如果参数用List接收的时候,用@RequestParam有什么区别?
同样的Postman传参
不加@RequestParam
加@RequestParam
在这里我上网查,得到的结论是:
不加注解只能接收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
报错:multipart/form-data……不支持
那么换成Json方式传参
而且@RequestBody参数只支持传入一个对象参数,它实际上是将输入流的body体作为一个整体进行转换,而body整体只有一份,解析完成之后会关闭输入流。如果传入多个参数则会报错。除非自定义参数解析器。
比如定义了两个@RequestBody就会报错
而如果定义了一个@RequestBody,但却接收两个参数,那么没有定义@RequestBody的参数将接收到为null