SpringMVC——对Ajax的处理(包含 JSON 类型)

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

SpringMVC——对Ajax的处理(包含JSON 类型)
一、首先要搞明白的一些事情。

1.从客户端来看,需要搞明白:
(1)要发送什么样格式的JSON 数据才能被服务器端的SpringMVC 很便捷的处理,怎么才能让我们写更少的代码,如何做好JSON 数据和实体之间的对应。

(2)如何组织这些发送的数据。

2.从服务器端来看,需要搞明白:
(1)SpringMVC 如何返回JSON 数据。

(2)SpringMVC 如何处理请求的复杂数据。

3.$.ajax 的几个参数:
(1)contentType:
contentType: 'application/json;charset=utf-8',作为请求头,用来告诉服务器消息的主体是序列化后的JSON 字符串。

除了低版本的ie 浏览器外,各大浏览器都原生支持JSON.stringify() 对对象进行序列化。

(2)dataType:预期服务器返回的数据类型。

4.SpringMVC 是如何处理JSON 数据的
5.总体的思想:
(1)SpringMVC 能完成的,尽量借助于SpringMVC,而不是我们手动的去解析。

(2)SpringMVC 解析不了的,尽量借助于第三方的Jar 包来解析。

(3)SpringMVC 和第三方Jar 包解决不了的时候,我们再自己去解析。

二、想要搞明白第一个问题,前提是先要搞明白第一个问题:SpringMVC 是如何处理JSON 数据的。

1.使用HttpMessageConverter<T> 来处理JSON 数据的。

Spring 的HttpMessageConverter<T> 负责将请求信息转换为一个对象,将对象输出为响应信息。

2.API
(1)boolean canRead(Class<?> clazz, MediaType mediaType);
转换器是否可将请求信息转换为clazz 类型的对象,同时支持指定的MIME 类型,如:text/html,application/json 等。

(2)boolean canWrite(Class<?> clazz, MediaType mediaType);
转换器是否可以将clazz 类型的对象写到响应中,响应支持的类型在mediaType 中定义。

(3)List<MediaType> getSupportedMediaTypes();
改转换器支持的MediaType 类型。

(4)T read(Class<? extends T> clazz, HttpInputMessage inputMessage);
将请求信息流转换为clazz 类型的对象。

(5)void write(T t, MediaType contentType, HttpOutputMessage outputMessage)。

将T 类型的对象写到响应输出流中,同时指定MediaType。

3.实现类
3.从上图可以看出,Spring 默认支持使用Jackson来处理JSON 问题。

添加Jackson Jar 包后,来看RequestMappingHadlerAdapter 装配的HttpMessageConverter:
导入的Jackson Jar 包:
4.具体的处理方法:
(1)使用@RequestBody 和HttpEntity<T> 对请求进行处理。

(2)使用@ResponseBody 和ResponseEntity<T> 对响应进行处理。

(3)@RequestBody 对处理方法的入参进行标注。

(4)@ResponseBody 对处理方法的签名进行标注。

(5)HttpEntity<T> 和ResponseEntity<T> 作为处理方法的入参使用。

具体的使用方法会在下面例子中进行说明。

5.@RequestBody 和@ResponseBody 是可以同时使用的。

三、上面简单介绍了SpringMVC 是怎么处理JSON 数据的,现在来看第二个问题:发送什么样格式的JSON 数据才能被服务器端的SpringMVC 很便捷的处理,这里主要指的是请求的JSON 字符串和实体的映射。

以一个简单的实体为例:Person
Person.java
(1)对于简单的一个Person 对象来说,我们甚至都不需要借助于JSON 就可以完成请求的数据与实体之间的映射。

请求:
复制代码
$("#testJson").click(function () {
$.ajax({
url: "testJson",
type: "post",
data: {
name : "abc",
age : "23"
},
success: function (result) {
console.log(result);
}
});
});
复制代码
handler 方法:
@RequestMapping("/testJson")
public Person testJson(Person person) {
System.out.println("person:" + person);
return person;
}
(2)对于Person数组来说,需要发送什么样的格式才能被SpringMVC 直接处理?
请求:
复制代码
$("#testJson6").click(function () {
$.ajax({
url: "testJson6",
type: "post",
data:'[{ "name": "Brett", "age":"12" }, { "name": "Jason", "age":"23" }, { "name": "Elliotte", "age":"33" }]',
contentType: "application/json; charset=utf-8",
success: function (result) {
console.log(result);
}
});
});
复制代码
handler 方法:
@RequestMapping("/testJson6")
public String testJson6(@RequestBody List<Person> persons) {
System.out.println("persons:" + persons);
return "success";
}
注意:
(1)需要指定"contentType",同时需要注意的是:发送的请求数据不在Form data 中,而是在Request Payload 中。

关于[Request Payload] ,在后面说明。

(2)必须要指定@RequestBody ,否则无法解析。

四、第三个问题:如何组织这些数据以及SpringMVC 如何处理这些数据,做好映射。

(1)说明:
上面说的两个例子,仅仅是最简单的一种形式。

现在对其进行扩展,在四里,所说的SpringMVC 如何处理这些数据,不仅仅指的是SpringMVC,也包括SpringMVC处理不了,使用第三方来处理,或者第三方处理不了,我自己来处理。

同时这里的数据也不仅仅指的JSON 类型的数据。

(2)对于非表单的Ajax 提交,这里只提供比较简单的一种方式。

还是以上面的Person 为例。

e1:
数据的组织与请求的发送:
复制代码
var personList = [];
personList.push({name: "李四",age: "23"});
personList.push({name: "张三",age: "12"});
$("#testJson5").click(function () {
$.ajax({
type: "POST",
url: "testJson5",
data: JSON.stringify(personList),//将对象序列化成JSON字符串
contentType: 'application/json;charset=utf-8', //设置请求头信息
success: function (data) {
},
error: function (res) {
}
});
});
复制代码
handler 方法:
@RequestMapping("/testJson5")
public String testJson5(@RequestBody List<Person> persons) { System.out.println(persons);
return "success";
}
(3)基于表单的Ajax 提交。

提供一个序列化方法:
复制代码
$.fn.serializeObject = function()
{
var o = {};
var a = this.serializeArray();
$.each(a, function() {
if (o[] !== undefined) {
if (!o[].push) {
o[] = [o[]];
}
o[].push(this.value || '');
} else {
o[] = this.value || '';
}
});
return o;
};
复制代码
还有一种序列化方式:
★单表单情况:
表单:
复制代码
<form action="" method="post">
First Name:<input type="text" name="firstName" maxlength="12" size="12"/> <br/>
Last Name:<input type="text" name="lastName" maxlength="36" size="12"/> <br/>
Gender:<br/>
Male:<input type="radio" name="gender" value="1"/><br/>
Female:<input type="radio" name="gender" value="0"/><br/>
Favorite Food:<br/>
Steak:<input type="checkbox" name="foods" value="Steak"/><br/>
Pizza:<input type="checkbox" name="foods" value="Pizza"/><br/>
Chicken:<input type="checkbox" name="foods" value="Chicken"/><br/>
<textarea wrap="physical" cols="20" name="quote" rows="5">Enter your favorite quote!</textarea><br/> Select a Level of Education:<br/>
<select name="education">
<option value="Jr.High">Jr.High</option>
<option value="HighSchool">HighSchool</option>
<option value="College">College</option>
</select><br/>
Select your favorite time of day:<br/>
<select size="3" name="tOfD">
<option value="Morning">Morning</option>
<option value="Day">Day</option>
<option value="Night">Night</option>
</select>
<p><input type="submit"/></p>
</form>
复制代码
对应的实体:
Student.java
e1:使用serializeObject()
序列化后的值:
JSON.stringify($('form').serializeObject()):
{"firstName":"jack","lastName":"lily","gender":"1","foods":["Pizza","Chicken"],"quote":"hello hello","education":"Jr.High","tOfD":"Day"}
请求:
复制代码
$(function() {
$('form').submit(function() {
$.ajax({
url : "testStudent",
data : JSON.stringify($('form').serializeObject()),
contentType : "application/json;charset=utf-8",
type : "post",
success : function (result) {
console.log(result);
}
});
return false;
});
});
复制代码
e11:SpringMVC自身进行处理
handler 方法:
@RequestMapping("/testStudent")
public String testStudent(@RequestBody Student student) {
System.out.println(student);
return "success";
}
e12:引入第三方Jar 包进行处理。

准备:
导入sl4j jar 包,同时添加JsonUtil 工具类。

JsonUtil.java
后端处理:
@RequestMapping("/testStudent")
public String testStudent(@RequestBody String inputBody) {
Student student = JsonUtil.fromJson(inputBody, Student.class);
System.out.println(student);
return "success";
}
都可以正常打印Student 对象。

e2:使用serialize()
序列化后的值:
$('form').serialize():
firstName=jack&lastName=lily&gender=1&foods=Pizza&foods=Chicken&quote=hello+hello&education=Jr.High&t OfD=Day
请求:
复制代码
$(function() {
$('form').submit(function() {
$.ajax({
url : "testStudent",
data : $('form').serialize(),
type : "post",
success : function (result) {
console.log(result);
}
});
return false;
});
});
复制代码
handler 方法:
@RequestMapping("/testStudent")
public String testStudent(Student student) {
System.out.println(student);
return "success";
}
可以正常打印Student 对象。

e1 和e2 对比说明:
e1提交的JSON 数据,e2 提交的不是JSON 格式的数据。

e1 的请求参数存放在[Request Payload] 中,而e2 的请求参数存放在Form Data 中。

★单表单复杂数据
表单还是上面的Student 表单,但是在表单外增加了:
<span id="amount">33</span>
需求是:通过Ajax 发送表单数据的同时,同时发送"amount" 。

经过测试,我就直接说结论了,有兴趣的童鞋可以自行探索,有新的发现欢迎和我交流。

结论:
不能对这样的数据,指定"contentType:application/json",否则后端SpringMVC或者第三方的Jar 包不能进行自动的解析,增加了解析的复杂度,所以将json 串传入后台,在后台进行解析。

e1:serializeObject()
请求:
复制代码
$(function() {
$('form').submit(function() {
$.ajax({
url : "testStudent",
data : {
amount : $("#amount").text(),
student : JSON.stringify($('form').serializeObject())
},
type : "post",
success : function (result) {
console.log(result);
}
});
return false;
});
});
复制代码
后端处理:使用第三方工具类进行解析
复制代码
@RequestMapping("/testStudent")
public String testStudent(@RequestParam("student") String studentStr, String amount) { Student student = JsonUtil.fromJson(studentStr, Student.class);
System.out.println("student:" + student);
System.out.println("amount:" + amount);
return "success";
}
复制代码
可以正常打印。

e2:serialize()
请求:
复制代码
$(function() {
$('form').submit(function() {
$.ajax({
url : "testStudent",
data : {
amount : $("#amount").text(),
student : $('form').serialize()
},
type : "post",
success : function (result) {
console.log(result);
}
});
return false;
});
});
复制代码
Handler 方法:
e1:尝试让SpringMVC 来解析:
@RequestMapping("/testStudent")
public String testStudent(@RequestParam("student") Student student, String amount) { System.out.println("student:" + student);
System.out.println("amount:" + amount);
return "success";
}
结果:请求无法到达handler 方法
e2:
@RequestMapping("/testStudent")
public String testStudent(Student student, String amount) {
System.out.println("student:" + student);
System.out.println("amount:" + amount);
return "success";
}
结果:请求可以正常到达目标Handler 方法,但是无法映射Student 对象。

方案:自己解析,编写自定义的类型转换器:
public class String2StudentConverter implements Converter<String, Student>{
@Override
public Student convert(String source) {
return InjectUtil.convert2Obj(source, Student.class);
}
}
这里我编写了一个通用的类型转换器:
用来转换形如:
firstName=jack&lastName=lily&gender=1&foods=Steak&foods=Pizza&quote=Enter+your+favorite+quote!&educa tion=Jr.High&tOfD=Day 到Student 对象。

复制代码
/**
* @author solverpeng
* @create 2016-08-22-17:37
*/
public final class InjectUtil<T> {
private static final Logger LOGGER = LoggerFactory.getLogger(InjectUtil.class);
public static <T> T converter2Obj(String source, Class<T> tClass) {
T t = null;
try {
t = tClass.newInstance();
Map<String, Object> params = new HashMap<String, Object>();
if(source != null && source.length() > 0) {
String[] fields = source.split("&");
for(String field : fields) {
String[] fieldKeyValue = field.split("\\=");
String fieldKey = fieldKeyValue[0];
String fieldValue = fieldKeyValue[1];
if (params.containsKey(fieldKey)) {
Object keyValueRetrieved = params.get(fieldKey);
if (keyValueRetrieved instanceof String) {
ArrayList<String> values = new ArrayList<>();
values.add(keyValueRetrieved.toString());
values.add(fieldValue);
params.put(fieldKey, values);
} else {
((ArrayList<String>) keyValueRetrieved).add(fieldValue);
}
} else {
params.put(fieldKey, fieldValue);
}
}
}
BeanUtils.populate(t, params);
} catch(InstantiationException | IllegalAccessException | InvocationTargetException e) {
Trace();
LOGGER.error("String convert to Bean failure!", e);
}
return t;
}
}
复制代码
不要忘记在SpringMVC 中添加自定义的转换器。

e3:也可以在handler 方法中来调用上面我编写的通用的类型转换器来完成解析。

@RequestMapping("/testStudent")
public String testStudent(@RequestParam("student") String studentStr, String amount) {
System.out.println("studentStr:" + studentStr);
System.out.println("amount:" + amount);
return "success";
}
说明:对于复杂数据来说,我们借助不了SpringMVC,只能借助于第三方,或是自己来编写解析器来解析。

★多表单一次提交
表单数据:
View Code
e1:
同时需要定义一个Students 类:
复制代码
public class Students {
private List<Student> students;
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
}
复制代码
请求:
复制代码
$('form').submit(function() {
$.ajax({
url : "testStudent",
data : JSON.stringify({
"students": [
$('#form1').serializeObject()
,
$('#form2').serializeObject()
]
}),
contentType:"application/json;charset=utf-8",
type : "post",
success : function (result) {
console.log(result);
}
});
return false;
});
复制代码
handler 方法:
复制代码
@RequestMapping("/testStudent")
public String testStudent(@RequestBody Students students) { for(Student student : students.getStudents()) {
System.out.println("student:" + student);
}
return "success";
}
复制代码
可以正常打印。

e2:不额外增加类,即不定义Students
请求:
复制代码
$('form').submit(function() {
$.ajax({
url : "testStudent",
data : JSON.stringify([
$('#form1').serializeObject(),
$('#form2').serializeObject()
]),
contentType:"application/json;charset=utf-8",
type : "post",
success : function (result) {
console.log(result);
}
});
return false;
});
复制代码
handler 方法:
e21:通过数组来接收
复制代码
@RequestMapping("/testStudent")
public String testStudent(@RequestBody Student[] students) { for(Student student : students) {
System.out.println("student: " + student);
}
return "success";
}
复制代码
e22:通过List 来接收
复制代码
@RequestMapping("/testStudent")
public String testStudent(@RequestBody List<Student> students) {
for(Student student : students) {
System.out.println("student: " + student);
}
return "success";
}
复制代码
★一个表单多个对象
表单对象如:
e1:
View Code
e2:
View Code
来看经过处理后的数据:
e1:
(1)JSON.stringify($('form').serializeObject()):
{"firstName":["jack","tom"],"lastName":["aa","lily"],"foods":["Steak","Pizza","Steak"],"quote":["Enter your favorite quote!","Enter your favorite quote!"],"education":["Jr.High","Jr.High"],"tOfD":["Day","Day"],"gender":"1"}
(2)$('form').serialize():
firstName=jack&lastName=aa&foods=Steak&foods=Pizza&quote=Enter+your+favorite+quote!&education=Jr.High &tOfD=Day&firstName=tom&lastName=lily&gender=1&foods=Steak&quote=Enter+your+favorite+quote!&educat ion=Jr.High&tOfD=Day
说明:
第一种是无法处理的,没办法分清数组中的值是属于哪个对象的。

第二种方式可以处理,但是需要编写自定义的类型转换器,这里不进行说明。

有兴趣的童鞋,请自行探索。

e2:
(1)JSON.stringify($('form').serializeObject()):
{"firstName[0]":"aa","lastName[0]":"bb","gender[0]":"1","foods[0]":"Pizza","quote[0]":"Enter your favorite
quote!","education[0]":"Jr.High","tOfD[0]":"Day",
"firstName[1]":"ds","lastName[1]":"cc","gender[1]":"1","foods[1]":["Steak","Pizza"],"quote[1]":"Enter your favorite quote!","education[1]":"Jr.High","tOfD[1]":"Day"}
(2)$('form').serialize():
firstName%5B0%5D=aa&lastName%5B0%5D=bb&gender%5B0%5D=1&foods%5B0%5D=Pizza&quote%5B0%5D=E nter+your+favorite+quote!&education%5B0%5D=Jr.High&tOfD%5B0%5D=Day&
firstName%5B1%5D=ds&lastName%5B1%5D=cc&gender%5B1%5D=1&foods%5B1%5D=Steak&foods%5B1%5D=P izza&quote%5B1%5D=Enter+your+favorite+quote!&education%5B1%5D=Jr.High&tOfD%5B1%5D=Day
说明:
第一种看着有规律可循,貌似可以进行解析,但是不是一个标准的JSON 格式的数据。

第二种甚至都出现了乱码,没有想到解析的办法。

来看看第一种,同样这里提供一种思路,因为实现起来比较费劲。

思路:使用正则
like this :
复制代码
Gson gson = new Gson();
String jsonInString = "{\"student[0].firstName\": \"asdf\",\"student[0].lastName\": \"sfd\",\"student[0].gender\": \"1\",\"student[0].foods\":[\"Steak\",\"Pizza\"],\"student[0].quote\": \"Enter your favorite quote!\",\"student[0].education\": \"Jr.High\",\"student[0].tOfD\": \"Day\",\"student[1].firstName\": \"sf\",\"student[1].lastName\": \"sdf\",\"student[1].gender\": \"1\",\"student[1].foods\": [\"Pizza\",\"Chicken\"],\"student[1].quote\": \"Enter your favorite quote!\",\"student[1].education\": \"Jr.High\",\"student[1].tOfD\": \"Night\"}";
String jsonWithoutArrayIndices = jsonInString.replaceAll("\\[\\d\\]", "").replaceAll("student.","");
String jsonAsCollection = "[" + jsonWithoutArrayIndices + "]";
String jsonAsValidCollection = jsonAsCollection.replaceAll(",\"student.firstName\"","},{\"student.firstName\""); System.out.println(ValidCollection);
Student[] students = gson.fromJson(jsonAsValidCollection, Student[].class);
System.out.println("-----------------------------------------------");
System.out.println(students[0]);
System.out.println("-----------------------------------------------");
复制代码
说明:
在真实的生产环境下我也没有遇到过这样的情况,所以这里就不往深挖了,等什么时候遇到这样的情况,我再来进行补充这篇文章。

总结:
上面这部分,介绍了项目中遇到的绝大部分SpringMVC 处理Ajax 的问题,也提供了多种方案进行选择,对于不常见的问题,也给出了思路。

是这篇文章最重要的部分。

五、服务器端的SpringMVC 如何返回JSON 类型的字符串。

请求:
复制代码
$("#testJson8").click(function () {
$.ajax({
url: "testReturnJsonValue",
type: "post",
success: function (result) {
console.log(result);
}
});
});
复制代码
1.返回单个对象
handler 方法:
复制代码
@ResponseBody
@RequestMapping("/testReturnJsonValue")
public Person testReturnJsonValue() {
Person person = new Person();
person.setName("lily");
person.setAge(23);
return person;
}
复制代码
在浏览器控制台正常打印了Person 对象。

注意:这里没有指定dataType。

2.返回多个对象
handler 方法:
复制代码
@ResponseBody
@RequestMapping("/testReturnJsonValue") public List<Person> testReturnJsonValue() {
List<Person> personList = new ArrayList<>();
Person person = new Person();
person.setName("lily");
person.setAge(23);
Person person2 = new Person();
person2.setName("lucy");
person2.setAge(33);
personList.add(person);
personList.add(person2);
return personList;
}
复制代码
在浏览器控制条正常打印了Person 数组。

3.返回Map
复制代码
@ResponseBody
@RequestMapping("/testReturnJsonValue") public Map<String, Person> testReturnJsonValue() { Map<String, Person> map = new HashMap<>();
Person person = new Person();
person.setName("lily");
person.setAge(23);
Person person2 = new Person();
person2.setName("lucy");
person2.setAge(33);
map.put("1", person);
map.put("2", person2);
return map;
}
复制代码
浏览器控制台输出:
4.在实际生产环境下的Ajax 返回值。

封装一个返回值类型:
复制代码
/**
* @author solverpeng
* @create 2016-08-30-17:58
*/
public class AjaxResult implements Serializable {
public static final String RESULT_CODE_0000 = "0000";
public static final String RESULT_CODE_0001 = "0001";
private String code;
private String message;
private Object data;
public AjaxResult() {
}
public String getCode() {
return this.code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return this.message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return this.data;
}
public void setData(Object data) {
this.data = data;
}
}
复制代码
实际使用:
复制代码
@ResponseBody
@RequestMapping("/testReturnJsonValue")
public AjaxResult testReturnJsonValue() {
AjaxResult ajaxResult = new AjaxResult();
try {
Map<String, Person> map = new HashMap<>();
Person person = new Person();
person.setName("lily");
person.setAge(23);
Person person2 = new Person();
person2.setName("lucy");
person2.setAge(33);
map.put("1", person);
map.put("2", person2);
ajaxResult.setData(map);
ajaxResult.setMessage("success!");
ajaxResult.setCode(AjaxResult.RESULT_CODE_0000);
} catch(ption e) {
e.printStackTrace();
ajaxResult.setMessage("fail!");
ajaxResult.setCode(AjaxResult.RESULT_CODE_0001);
}
return ajaxResult;
}。

相关文档
最新文档