SpringMVC参数绑定入门必看
开篇引言
前面已经写过 SSM 三大框架的一些入门文章,在 SpringMVC 部分,关于参数的绑定提的不是太多,重新整理了一下,就当做一个补充,时间匆匆,可能会有一些错误,大家可以共同交流,一起探讨!
注:下面的文章,重点还是参数绑定的使用,关于导包或者一些注解的讲解,我没有多说,之前的文章一些常用的也都还介绍过,如果有必要,我再整理一篇关于注解的总结也可以哈 ~
【万字长文】Spring MVC 层层递进轻松入门 !
https://juejin.im/post/5e781ca3e51d452726153fa0
(一) 基本类型、包装类型参数绑定
我们假定要请求的参数为 age ,那么我们有两种选择 :即 ① 传入基本类型 int ② 传入包装类型 Integer ,我们这一块的讲解,就用它们两个来讲解
注:我们将重心放在参数绑定上,项目名或者Controller上的@RequestMapping注解,我都没加
(1) 基本类型 int
@RequestMapping("baseType.do")
@ResponseBody
public String baseType(int age) {
return "age:" + age;
}
http://localhost:8080/baseType.do?age=30
当我们请求时,返回结果:age:30
- 这种情况下,首先 key 值必须传入,否则会报 500 错误,提示当前 age 不能为空
- 其次,参数只能为 int 类型,否则报 400 参数异常错误
这里有一个问题需要提一下,大家应该知道一个注解 @RequestParam ,我们是否可通过这个注解的 required 属性,帮助我们规避这个请求参数为空的问题呢?
答案是否定的,虽然这个注解设置 required = false 后不传值后台也不会报错,但是如果其中指定了基本数据类型,例如我们代码中的 int 这个时候如果不传值是依旧会报一个 500 错误
因为其不传值就赋 null,但是 int 类型却不能为null
所以想要规避这个参数为空的问题,我们就可以选择包装类型 Integer
(2) 包装类型
@RequestMapping("packingType.do")
@ResponseBody
public String packingType(Integer age) {
return "age:" + age;
}
http://localhost:8080/packingType.do?age=30
正常返回:age:30
http://localhost:8080/packingType.do
http://localhost:8080/packingType.do?
http://localhost:8080/packingType.do?=
参数为空不报错,均返回:age:null
- 可以不传 key,后台接收到的数据则为 age=null
- 所以开发中,对于参数可能为空的数据,建议使用包装类型
- 当然,我们也可以使用 @RequestParam 注解 来设置是否请求中必须包含该参数,此注解默认就是必须传参,否则报错
(二) 对象的参数绑定
(1) 多层级对象的绑定
什么是多层级对象,先别急,先看一个最基础的例子
我们首先创建一个用户类
public class User {
private String id;
private String name;
......补充其 get set toString 方法
}
直接在参数中写上对应 User 类型就可以了
@RequestMapping("objectType.do")
@ResponseBody
public String objectType(User user) {
return user.toString();
}
http://localhost:8080/objectType.do?id=001&name=Steven
返回结果:User{uid='001', name='Steven'}
如果这个时候,我创建一个新的类 UserDetails
public class UserDetails {
private Integer age;
private String address;
......补充其 get set toString 方法
}
在 User 类中引入这个类,这种情况又该如何绑定参数呢
public class User {
private String id;
private String name;
private UserDetails userDetails;
......补充其 get set toString 方法
}
http://localhost:8080/objectType.do?id=1&name=Steven&userDetails.age=20&userDetails.address=BeiJing
返回结果:User{uid='1', name='Steven', userDetails=UserDetails{age=20, address='BeiJing'}}
- 对于引入的对象成员复赋值,格式就例如:
userDetails.address=xxxxx
- 这里地址我没用中文,是因为我是直接返回的,没经过编码的处理,不然会显示 ? ?
(2) 同属性对象参数绑定
如果我们想要直接接收两个对象,有时候免不了有相同的成员,例如我们的 User 和 Student 类中均含有
Integer id 、String name 两个成员,我们试着请求一下
@RequestMapping("objectType2.do")
@ResponseBody
public String objectType2(User user, Student student) {
return user.toString() + " " + student.toString();
}
http://localhost:8080/objectType2.do?id=8&name=Steven
返回结果:User{id='8', name='Steven'} Student{id='8', name='Steven'}
可以看到,两个对象的值都被赋上了,但是,大部分情况下,不同的对象的值一般都是不同的,为此,我们还有解决办法
@InitBinder 注解可以帮助我们分开绑定,下面的代码也就是说分别给 user、student 指定一个前缀
@InitBinder("user")
public void initUser(WebDataBinder binder) {
binder.setFieldDefaultPrefix("user.");
}
@InitBinder("student")
public void initStudent(WebDataBinder binder) {
binder.setFieldDefaultPrefix("stu.");
}
http://localhost:8080/objectType2.do?user.id=1&name=Steven&stu.id=002
当发起这样一个请求后,我们分别指定了 user 和 student 的 id 值,而 name 则是同样的 Steven
返回结果:User{id='1', name='Steven', userDetails=null} Student{id='2', name='Steven'}
(三) 数组类型参数绑定
@RequestMapping("arrayType.do")
@ResponseBody
public String arrayType(String[] nickname) {
StringBuilder sb = new StringBuilder();
for (String s : nickname) {
sb.append(s).append(", ");
}
return sb.toString();
}
http://localhost:8080/arrayType.do?nickname=Jack&nickname=Steven&nickname=Tom
返回结果:Jack, Steven, Tom,
(四) 集合类型参数绑定
(1) List 类型
集合是不能直接进行参数绑定的,所以我们需要创建出一个类,然后在类中进行对 List 的参数绑定
首先创建 UserList 类,其中我为了演示,只放了 private List<User> users
补充好 get set toString 方法
控制层方法中,参数就是这个创建出来的类
@RequestMapping("listType.do")
@ResponseBody
public String listType(UserList userList) {
return userList.toString();
}
http://localhost:8080/listType.do?users[0].id=1&users[0].name=Jack&users[1].id=2&users[1].name=Marry
我们的请求,分别将两个user信息存入了 List<User> users
中
特别注意:如果你的 Tomcat 版本是 7.0 左右 那么上述请求是没问题的,但是如果版本比较高,例如我本身所用的 Tomcat 8.5 ,如果执行上述请求就会报 400 错误
HTTP Status 400 – Bad Request
Message Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
这是因为Tomcat高的版本地址中不能使用“[”和“]” ,我们可以将其换成对应的16进制,即 “[” 换成 %5B,“]” 换成 %5D
http://localhost:8080/listType.do?users%5B0%5D.id=1&users%5B0%5D.name=Jack&users%5B1%5D.id=2&users%5B1%5D.name=Marry
或者直接用 post 请求也是可以的哈
(2) Map 类型
map 类型是一样的套路,我们先创建一个 UserMap类,然后在其中声明 private Map<String,User> users
进而绑定参数
@RequestMapping("mapType.do")
@ResponseBody
public String mapType(UserMap userMap) {
return userMap.toString();
}
http://localhost:8080/mapType.do?users['userA'].id=1&users['userA'].name=Jack&users['userB'].id=2&users['userB'].name=Tom
同样 “[]” 会遇到上面的错误,所以如果想要在地址栏请求访问,就需要替换字符,或者发起一个 post 请求
http://localhost:8080/mapType.do?users%5B%27userA%27%5D.id=1&users%5B%27userA%27%5D.name=Jack&users%5B%27userB%27%5D.id=2&users%5B%27userB%27%5D.name=Tom
返回结果:UserMap{users={userA=User{id='1', name='Jack'}, userB=User{id='2', name='Tom'}}}
(五) JSON 参数绑定
除了前面表单等提交的方式,我们还有一种ajax的提交方式,常常用来向后端传递以及接受 json 格式的数据,关于 json 字符串和对象之间的转换会用到下面的 jar包
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.10.0</version>
</dependency>
这一块的演示,我们创建一个 Admin 实体类
public class Admin {
private Integer id;
private String name;
......补充其 get set toString 方法
}
(1) 实体参数绑定
当 ajax 传递的参数很多的时候,使用参数名匹配,会非常麻烦,如果请求的参数在后台中有一个匹配的实体类,我们就可以选择前台传一个 json 到后台,后台使用匹配的实体类进行接收
提交 JSON: {"id": "37","name": "张三"}
$(function () {
$("#btn").click(function () {
//发送ajax请求
$.ajax({
url:"ajaxType1.do",
contentType:"application/json",
data:'{"id":"37","name":"张三"}',
dataType:"json",
type:"post",
success:function (data) {
//解析响应数据
alert(data.id);
alert(data.name);
}
})
});
});
Contoller
//用实体接参数
@RequestMapping("ajaxType1.do")
@ResponseBody
public Admin ajaxType1(@RequestBody Admin admin) {
System.out.println(admin.toString());
admin.setName("测试管理员");
return admin;
}
后端参数中的 admin 就会被绑定好参数,供开发者使用
@RequestBody
注解常用来处理 content-type
不是默认的application/x-www-form-urlcoded
编码的内容,说常处理application/json
类型
(2) Map 参数绑定
还有一种情况,那就是请求的参数仍然挺多,但是后台也没有一个合适的实体进行匹配,我们也可以考虑使用map来接收
依旧提交 JSON: {"id": "37","name": "张三"}
$(function () {
$("#btn").click(function () {
//发送ajax请求
$.ajax({
url:"ajaxType1.do",
contentType:"application/json",
data:'{"id":"37","name":"张三"}',
dataType:"json",
type:"post",
success:function (data) {
//解析响应数据
alert(data.id);
alert(data.name);
}
})
});
});
但是 Controller 中我们使用 map 来进行接收,然后简单给了一个实例,将map值封装到 Admin 对象中,然后返回到前端去
@RequestMapping("ajaxType3.do")
@ResponseBody
public Admin ajaxType3(@RequestBody Map<String, String> map) {
Integer id = null;
String name = null;
if (map.containsKey("id")){
id = Integer.parseInt(map.get("id"));
}
if (map.containsKey("name")){
name = map.get("name");
}
Admin admin = new Admin();
admin.setId(id);
admin.setName(name);
return admin;
}
(3) List 参数绑定
同样的,我们还可以使用 list 方式进行接收,它时以 json 数组的形式传递的
var listType=[];
var admin={};
admin.id=1;
admin.name='汤姆';
listType.push(admin);
var admin2={};
admin2.id=2;
admin2.name='杰克';
listType.push(admin2);
$(function () {
$("#btn").click(function () {
//发送ajax请求
$.ajax({
url:"ajaxType1.do",
contentType:"application/json",
data:JSON.stringify(listType),
dataType:"json",
type:"post",
success:function (data) {
//解析响应数据
alert(data.id);
alert(data.name);
}
})
});
});
去后台看一下
@RequestMapping("ajaxType5.do")
@ResponseBody
public void ajaxType5(@RequestBody List<Admin> list) {
System.out.println(list);
for (Admin admin : list){
System.out.println(admin.getId() + " " + admin.getName());
}
}
看一下控制台的输出:
[Admin{id='1', name='汤姆'}, Admin{id='2', name='杰克'}]
1 汤姆
2 杰克
(4) 补充
这是用来提交的表单
<form id="ajaxForm" method="post">
id:<input type="text" name="id">
name:<input type="text" name="name">
</form>
这是 ajax请求,我们也常用$("#ajaxForm").serialize()
进行一个表单的序列化,然后提交,但是它只是将Form序列化拼接成了简单的字符串,并不是JSON格式,它是例如这样的:
id=111&name=Steven
所以刚才所说的json那一套就不管用了
$(function () {
$("#btn").click(function () {
//发送ajax请求
$.ajax({
url:"ajaxType1.do",
contentType:"application/json",
data:$("#ajaxForm").serialize(),
dataType:"json",
type:"post",
success:function (data) {
//解析响应数据
alert(data.id);
alert(data.name);
}
})
});
});
如果想要使用序列化,同时还想要传递 json 格式到后台,也不是没办法
我们需要添加一个方法
$.fn.serializeObject = function() {
var o = {};
var a = this.serializeArray();
$.each(a, function() {
if (o[this.name]) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
};
同时将下面 ajax 中的 data修改为
data:JSON.stringify($("#ajaxForm").serializeObject()),
后台就能获取到 json 格式了
{"id":"111","name":"Steven"}
(六) XML 参数绑定
后台非常简单,和前面没什么区别,我们需要在实体中进行一些操作
@RequestMapping("xmlType.do")
@ResponseBody
public String xmlType(@RequestBody Student student) {
return student.toString();
}
这种情况下,我们需要借助一个jar包 —— spring-oxm,自己可以导入一下
然后我们需要在接受的实体那里,添加 @XmlRootElement 和 @XmlElement 注解,来代表根节点和子节点
package cn.ideal.Object;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "student")
public class Student {
private Integer id;
private String name;
@XmlElement(name = "id")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@XmlElement(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
'}';
}
}
例如我们xml为
<?xml version="1.0" encoding="UTF-8" ?>
<student>
<id>66</id>
<name>Steven</name>
</student>
然后发起请求,注意将Content-Type 改为 application/xml
返回的结果:Student{id='66', name='Steven'}
(七) 结尾
如果文章中有什么不足,欢迎大家留言交流,感谢朋友们的支持!
如果能帮到你的话,那就来关注我吧!如果您更喜欢微信文章的阅读方式,可以关注我的公众号
在这里的我们素不相识,却都在为了自己的梦而努力 ❤
一个坚持推送原创开发技术文章的公众号:理想二旬不止