SpringMVC—对Ajax的处理(含 JSON 类型)
Posted Ipeter
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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.实现类
Spring 默认支持使用 Jackson来处理 JSON 问题。
导入的 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 /** * @author solverpeng * @create 2016-08-12-10:50 */public class Person { private String name; private Integer age; public Person() { } public Person(String name, Integer age) { this.name = name; this.age = age; } @NotBlank(message = "人名不能为空") public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name=\'" + name + \'\\\'\' + ", age=" + age + \'}\'; } } (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[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 || \'\'; } }); 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 /** * @author solverpeng * @create 2016-08-16-11:14 */public class Student { private String firstName; private String lastName; private Integer gender; private List<String> foods; private String quote; private String education; private String tOfD; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Integer getGender() { return gender; } public void setGender(Integer gender) { this.gender = gender; } public List<String> getFoods() { return foods; } public void setFoods(List<String> foods) { this.foods = foods; } public String getQuote() { return quote; } public void setQuote(String quote) { this.quote = quote; } public String getEducation() { return education; } public void setEducation(String education) { this.education = education; } public String gettOfD() { return tOfD; } public void settOfD(String tOfD) { this.tOfD = tOfD; } @Override public String toString() { return "Student{" + "firstName=\'" + firstName + \'\\\'\' + ", lastName=\'" + lastName + \'\\\'\' + ", gender=" + gender + ", foods=" + foods + ", quote=\'" + quote + \'\\\'\' + ", education=\'" + education + \'\\\'\' + ", tOfD=\'" + tOfD + \'\\\'\' + \'}\'; } } 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 public final class JsonUtil { private static final Logger LOGGER = LoggerFactory.getLogger(JsonUtil.class); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); /** * 将 POJO 转换为 JSON */ public static <T> String toJson(T obj) { String json; try { json = OBJECT_MAPPER.writeValueAsString(obj); } catch(JsonProcessingException e) { LOGGER.error("convert POJO to JSON failure", e); throw new RuntimeException(e); } return json; } /** * 将 JSON 转换为 POJO */ public static <T> T fromJson(String json, Class<T> type) { T pojo; try { pojo = OBJECT_MAPPER.readValue(json, type); } catch(IOException e) { LOGGER.error("convert JSON to POJO failure", e); throw new RuntimeException(e); } return pojo; } } 后端处理: @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"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); } }); 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); } }
以上是关于SpringMVC—对Ajax的处理(含 JSON 类型)的主要内容,如果未能解决你的问题,请参考以下文章
SpringMVC——对Ajax的处理(包含 JSON 类型)
Spring注解处理Ajax请求-JSON格式[系统架构:Spring+SpringMVC+MyBatis+MySql]