配色: 字号:
SpringMVC——对Ajax的处理(包含 JSON 类型)
2016-10-28 | 阅:  转:  |  分享 
  
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(Classclazz,MediaTypemediaType);



转换器是否可将请求信息转换为clazz类型的对象,同时支持指定的MIME类型,如:text/html,application/json等。



(2)booleancanWrite(Classclazz,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;

}

献花(0)
+1
(本文系thedust79首藏)