前面已经写过 SSM 三大框架的一些入门文章,在 SpringMVC 部分,关于参数的绑定提的不是太多,重新整理了一下,就当做一个补充,时间匆匆,可能会有一些错误,大家可以共同交流,一起探讨!
注:下面的文章,重点还是参数绑定的使用,关于导包或者一些注解的讲解,我没有多说,之前的文章一些常用的也都还介绍过,如果有必要,我再整理一篇关于注解的总结也可以哈 ~
【万字长文】Spring MVC 层层递进轻松入门 !
https://juejin.im/post/5e781c...
我们假定要请求的参数为 age ,那么我们有两种选择 :即 ① 传入基本类型 int ② 传入包装类型 Integer ,我们这一块的讲解,就用它们两个来讲解
注:我们将重心放在参数绑定上,项目名或者Controller上的@RequestMapping注解,我都没加
@RequestMapping("baseType.do") @ResponseBody public String baseType(int age) { return "age:" + age; }
http://localhost:8080/baseType.do?age=30
当我们请求时,返回结果:age:30
这里有一个问题需要提一下,大家应该知道一个注解 @RequestParam ,我们是否可通过这个注解的 required 属性,帮助我们规避这个请求参数为空的问题呢?
答案是否定的,虽然这个注解设置 required = false 后不传值后台也不会报错,但是如果其中指定了基本数据类型,例如我们代码中的 int 这个时候如果不传值是依旧会报一个 500 错误
因为其不传值就赋 null,但是 int 类型却不能为null
所以想要规避这个参数为空的问题,我们就可以选择包装类型 Integer
@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
什么是多层级对象,先别急,先看一个最基础的例子
我们首先创建一个用户类
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
如果我们想要直接接收两个对象,有时候免不了有相同的成员,例如我们的 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,
集合是不能直接进行参数绑定的,所以我们需要创建出一个类,然后在类中进行对 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 错误
MessageInvalid 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 请求也是可以的哈
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'}}}
除了前面表单等提交的方式,我们还有一种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 方法 }
当 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); } }) }); });
//用实体接参数 @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
类型
还有一种情况,那就是请求的参数仍然挺多,但是后台也没有一个合适的实体进行匹配,我们也可以考虑使用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; }
同样的,我们还可以使用 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 杰克
这是用来提交的表单
<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"}
后台非常简单,和前面没什么区别,我们需要在实体中进行一些操作
@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'}
如果文章中有什么不足,欢迎大家留言交流,感谢朋友们的支持!
如果能帮到你的话,那就来关注我吧!如果您更喜欢微信文章的阅读方式,可以关注我的公众号
一个坚持推送原创开发技术文章的公众号:理想二旬不止