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来处理JSON数据的。
Spring的HttpMessageConverter负责将请求信息转换为一个对象,将对象输出为响应信息。
2.API
(1)booleancanRead(Class>clazz,MediaTypemediaType);
转换器是否可将请求信息转换为clazz类型的对象,同时支持指定的MIME类型,如:text/html,application/json等。
(2)booleancanWrite(Class>clazz,MediaTypemediaType);
转换器是否可以将clazz类型的对象写到响应中,响应支持的类型在mediaType中定义。
(3)ListgetSupportedMediaTypes();
改转换器支持的MediaType类型。
(4)Tread(Classclazz,HttpInputMessageinputMessage);
将请求信息流转换为clazz类型的对象。
(5)voidwrite(Tt,MediaTypecontentType,HttpOutputMessageoutputMessage)。
将T类型的对象写到响应输出流中,同时指定MediaType。
3.实现类
3.从上图可以看出,Spring默认支持使用Jackson来处理JSON问题。添加JacksonJar包后,来看RequestMappingHadlerAdapter装配的HttpMessageConverter:
导入的JacksonJar包:
4.具体的处理方法:
(1)使用@RequestBody和HttpEntity对请求进行处理。
(2)使用@ResponseBody和ResponseEntity对响应进行处理。
(3)@RequestBody对处理方法的入参进行标注。
(4)@ResponseBody对处理方法的签名进行标注。
(5)HttpEntity和ResponseEntity作为处理方法的入参使用。
具体的使用方法会在下面例子中进行说明。
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")
publicPersontestJson(Personperson){
System.out.println("person:"+person);
returnperson;
}
(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")
publicStringtestJson6(@RequestBodyListpersons){
System.out.println("persons:"+persons);
return"success";
}
注意:
(1)需要指定"contentType",同时需要注意的是:发送的请求数据不在Formdata中,而是在RequestPayload中。关于[RequestPayload],在后面说明。
(2)必须要指定@RequestBody,否则无法解析。
四、第三个问题:如何组织这些数据以及SpringMVC如何处理这些数据,做好映射。
(1)说明:
上面说的两个例子,仅仅是最简单的一种形式。现在对其进行扩展,在四里,所说的SpringMVC如何处理这些数据,不仅仅指的是SpringMVC,也包括SpringMVC处理不了,使用第三方来处理,或者第三方处理不了,我自己来处理。
同时这里的数据也不仅仅指的JSON类型的数据。
(2)对于非表单的Ajax提交,这里只提供比较简单的一种方式。还是以上面的Person为例。
e1:
数据的组织与请求的发送:
复制代码
varpersonList=[];
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")
publicStringtestJson5(@RequestBodyListpersons){
System.out.println(persons);
return"success";
}
(3)基于表单的Ajax提交。
提供一个序列化方法:
复制代码
$.fn.serializeObject=function()
{
varo={};
vara=this.serializeArray();
$.each(a,function(){
if(o[this.name]!==undefined){
if(!o[this.name].push){
o[this.name]=[o[this.name]];
}
o[this.name].push(this.value||'''');
}else{
o[this.name]=this.value||'''';
}
});
returno;
};
复制代码
还有一种序列化方式:
★单表单情况:
表单:
复制代码
FirstName:
LastName:
Gender:
Male:
Female:
FavoriteFood:
Steak:
Pizza:
Chicken:
Enteryourfavoritequote!
SelectaLevelofEducation:
Jr.High
HighSchool
College
Selectyourfavoritetimeofday:
Morning
Day
Night
复制代码
对应的实体:
Student.java
e1:使用serializeObject()
序列化后的值:
JSON.stringify($(''form'').serializeObject()):
{"firstName":"jack","lastName":"lily","gender":"1","foods":["Pizza","Chicken"],"quote":"hellohello","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);
}
});
returnfalse;
});
});
复制代码
e11:SpringMVC自身进行处理
handler方法:
@RequestMapping("/testStudent")
publicStringtestStudent(@RequestBodyStudentstudent){
System.out.println(student);
return"success";
}
e12:引入第三方Jar包进行处理。
准备:
导入sl4jjar包,同时添加JsonUtil工具类。
JsonUtil.java
后端处理:
@RequestMapping("/testStudent")
publicStringtestStudent(@RequestBodyStringinputBody){
Studentstudent=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"e=hello+hello&education=Jr.High&tOfD=Day
请求:
复制代码
$(function(){
$(''form'').submit(function(){
$.ajax({
url:"testStudent",
data:$(''form'').serialize(),
type:"post",
success:function(result){
console.log(result);
}
});
returnfalse;
});
});
复制代码
handler方法:
@RequestMapping("/testStudent")
publicStringtestStudent(Studentstudent){
System.out.println(student);
return"success";
}
可以正常打印Student对象。
e1和e2对比说明:
e1提交的JSON数据,e2提交的不是JSON格式的数据。e1的请求参数存放在[RequestPayload]中,而e2的请求参数存放在FormData中。
★单表单复杂数据
表单还是上面的Student表单,但是在表单外增加了:
33
需求是:通过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);
}
});
returnfalse;
});
});
复制代码
后端处理:使用第三方工具类进行解析
复制代码
@RequestMapping("/testStudent")
publicStringtestStudent(@RequestParam("student")StringstudentStr,Stringamount){
Studentstudent=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);
}
});
returnfalse;
});
});
复制代码
Handler方法:
e1:尝试让SpringMVC来解析:
@RequestMapping("/testStudent")
publicStringtestStudent(@RequestParam("student")Studentstudent,Stringamount){
System.out.println("student:"+student);
System.out.println("amount:"+amount);
return"success";
}
结果:请求无法到达handler方法
e2:
@RequestMapping("/testStudent")
publicStringtestStudent(Studentstudent,Stringamount){
System.out.println("student:"+student);
System.out.println("amount:"+amount);
return"success";
}
结果:请求可以正常到达目标Handler方法,但是无法映射Student对象。
方案:自己解析,编写自定义的类型转换器:
publicclassString2StudentConverterimplementsConverter{
@Override
publicStudentconvert(Stringsource){
returnInjectUtil.convert2Obj(source,Student.class);
}
}
这里我编写了一个通用的类型转换器:
用来转换形如:
firstName=jack&lastName=lily&gender=1&foods=Steak&foods=Pizza"e=Enter+your+favorite+quote!&education=Jr.High&tOfD=Day到Student对象。
复制代码
/
@authorsolverpeng
@create2016-08-22-17:37
/
publicfinalclassInjectUtil{
privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(InjectUtil.class);
publicstaticTconverter2Obj(Stringsource,ClasstClass){
Tt=null;
try{
t=tClass.newInstance();
Mapparams=newHashMap();
if(source!=null&&source.length()>0){
String[]fields=source.split("&");
for(Stringfield:fields){
String[]fieldKeyValue=field.split("\\=");
StringfieldKey=fieldKeyValue[0];
StringfieldValue=fieldKeyValue[1];
if(params.containsKey(fieldKey)){
ObjectkeyValueRetrieved=params.get(fieldKey);
if(keyValueRetrievedinstanceofString){
ArrayListvalues=newArrayList<>();
values.add(keyValueRetrieved.toString());
values.add(fieldValue);
params.put(fieldKey,values);
}else{
((ArrayList)keyValueRetrieved).add(fieldValue);
}
}else{
params.put(fieldKey,fieldValue);
}
}
}
BeanUtils.populate(t,params);
}catch(InstantiationException|IllegalAccessException|InvocationTargetExceptione){
e.printStackwww.wang027.comTrace();
LOGGER.error("StringconverttoBeanfailure!",e);
}
returnt;
}
}
复制代码
不要忘记在SpringMVC中添加自定义的转换器。
e3:也可以在handler方法中来调用上面我编写的通用的类型转换器来完成解析。
@RequestMapping("/testStudent")
publicStringtestStudent(@RequestParam("student")StringstudentStr,Stringamount){
System.out.println("studentStr:"+studentStr);
System.out.println("amount:"+amount);
return"success";
}
说明:对于复杂数据来说,我们借助不了SpringMVC,只能借助于第三方,或是自己来编写解析器来解析。
★多表单一次提交
表单数据:
ViewCode
e1:
同时需要定义一个Students类:
复制代码
publicclassStudents{
privateListstudents;
publicListgetStudents(){
returnstudents;
}
publicvoidsetStudents(Liststudents){
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);
}
});
returnfalse;
});
复制代码
handler方法:
复制代码
@RequestMapping("/testStudent")
publicStringtestStudent(@RequestBodyStudentsstudents){
for(Studentstudent: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);
}
});
returnfalse;
});
复制代码
handler方法:
e21:通过数组来接收
复制代码
@RequestMapping("/testStudent")
publicStringtestStudent(@RequestBodyStudent[]students){
for(Studentstudent:students){
System.out.println("student:"+student);
}
return"success";
}
复制代码
e22:通过List来接收
复制代码
@RequestMapping("/testStudent")
publicStringtestStudent(@RequestBodyListstudents){
for(Studentstudent:students){
System.out.println("student:"+student);
}
return"success";
}
复制代码
★一个表单多个对象
表单对象如:
e1:
ViewCode
e2:
ViewCode
来看经过处理后的数据:
e1:
(1)JSON.stringify($(''form'').serializeObject()):
{"firstName":["jack","tom"],"lastName":["aa","lily"],"foods":["Steak","Pizza","Steak"],"quote":["Enteryourfavoritequote!","Enteryourfavoritequote!"],"education":["Jr.High","Jr.High"],"tOfD":["Day","Day"],"gender":"1"}
(2)$(''form'').serialize():
firstName=jack&lastName=aa&foods=Steak&foods=Pizza"e=Enter+your+favorite+quote!&education=Jr.High&tOfD=Day&firstName=tom&lastName=lily&gender=1&foods=Steak"e=Enter+your+favorite+quote!&education=Jr.High&tOfD=Day
说明:
第一种是无法处理的,没办法分清数组中的值是属于哪个对象的。
第二种方式可以处理,但是需要编写自定义的类型转换器,这里不进行说明。有兴趣的童鞋,请自行探索。
e2:
(1)JSON.stringify($(''form'').serializeObject()):
{"firstName[0]":"aa","lastName[0]":"bb","gender[0]":"1","foods[0]":"Pizza","quote[0]":"Enteryourfavoritequote!","education[0]":"Jr.High","tOfD[0]":"Day",
"firstName[1]":"ds","lastName[1]":"cc","gender[1]":"1","foods[1]":["Steak","Pizza"],"quote[1]":"Enteryourfavoritequote!","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"e%5B0%5D=Enter+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=Pizza"e%5B1%5D=Enter+your+favorite+quote!&education%5B1%5D=Jr.High&tOfD%5B1%5D=Day
说明:
第一种看着有规律可循,貌似可以进行解析,但是不是一个标准的JSON格式的数据。
第二种甚至都出现了乱码,没有想到解析的办法。
来看看第一种,同样这里提供一种思路,因为实现起来比较费劲。
思路:使用正则
likethis:
复制代码
Gsongson=newGson();
StringjsonInString="{\"student[0].firstName\":\"asdf\",\"student[0].lastName\":\"sfd\",\"student[0].gender\":\"1\",\"student[0].foods\":[\"Steak\",\"Pizza\"],\"student[0].quote\":\"Enteryourfavoritequote!\",\"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\":\"Enteryourfavoritequote!\",\"student[1].education\":\"Jr.High\",\"student[1].tOfD\":\"Night\"}";
StringjsonWithoutArrayIndices=jsonInString.replaceAll("\\[\\d\\]","").replaceAll("student.","");
StringjsonAsCollection="["+jsonWithoutArrayIndices+"]";
StringjsonAsValidCollection=jsonAsCollection.replaceAll(",\"student.firstName\"","},{\"student.firstName\"");
System.out.println(jsonAswww.baiyuewang.netValidCollection);
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")
publicPersontestReturnJsonValue(){
Personperson=newPerson();
person.setName("lily");
person.setAge(23);
returnperson;
}
复制代码
在浏览器控制台正常打印了Person对象。
注意:这里没有指定dataType。
2.返回多个对象
handler方法:
复制代码
@ResponseBody
@RequestMapping("/testReturnJsonValue")
publicListtestReturnJsonValue(){
ListpersonList=newArrayList<>();
Personperson=newPerson();
person.setName("lily");
person.setAge(23);
Personperson2=newPerson();
person2.setName("lucy");
person2.setAge(33);
personList.add(person);
personList.add(person2);
returnpersonList;
}
复制代码
在浏览器控制条正常打印了Person数组。
3.返回Map
复制代码
@ResponseBody
@RequestMapping("/testReturnJsonValue")
publicMaptestReturnJsonValue(){
Mapmap=newHashMap<>();
Personperson=newPerson();
person.setName("lily");
person.setAge(23);
Personperson2=newPerson();
person2.setName("lucy");
person2.setAge(33);
map.put("1",person);
map.put("2",person2);
returnmap;
}
复制代码
浏览器控制台输出:
4.在实际生产环境下的Ajax返回值。
封装一个返回值类型:
复制代码
/
@authorsolverpeng
@create2016-08-30-17:58
/
publicclassAjaxResultimplementsSerializable{
publicstaticfinalStringRESULT_CODE_0000="0000";
publicstaticfinalStringRESULT_CODE_0001="0001";
privateStringcode;
privateStringmessage;
privateObjectdata;
publicAjaxResult(){
}
publicStringgetCode(){
returnthis.code;
}
publicvoidsetCode(Stringcode){
this.code=code;
}
publicStringgetMessage(){
returnthis.message;
}
publicvoidsetMessage(Stringmessage){
this.message=message;
}
publicObjectgetData(){
returnthis.data;
}
publicvoidsetData(Objectdata){
this.data=data;
}
}
复制代码
实际使用:
复制代码
@ResponseBody
@RequestMapping("/testReturnJsonValue")
publicAjaxResulttestReturnJsonValue(){
AjaxResultajaxResult=newAjaxResult();
try{
Mapmap=newHashMap<>();
Personperson=newPerson();
person.setName("lily");
person.setAge(23);
Personperson2=newPerson();
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(Excewww.wang027.comptione){
e.printStackTrace();
ajaxResult.setMessage("fail!");
ajaxResult.setCode(AjaxResult.RESULT_CODE_0001);
}
returnajaxResult;
}
|
|