springMVC-12 处理json和HttpMessageConverter<T>
Json拾遗
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式
1.回顾json语法
JSON的格式主要有两种结构:
1) 对象(Object):表示为键值对(key: value)的无序集合,使用花括号{}包围。键值对之间用逗号分隔,键和值之间用冒号分隔。
键必须是字符串,用双引号括起来。
值可以是字符串、数字、布尔值、数组、对象或null。
2)数组(Array):表示值的有序集合,使用方括号[]包围。值之间用逗号分隔。
数组中的值可以是字符串、数字、布尔值、数组、对象或null。
注意:JSON中不允许有注释,且字符串必须使用双引号(不能使用单引号)。
下面是一个JSON对象的例子:
{
"name": "张三",
"age": 30,
"isStudent": false,
"hobbies": ["篮球", "阅读", "旅游"],
"address": {
"city": "北京",
"street": "朝阳路"
},
"nullValue": null
}
在这个例子中:
- "name"是 字符串
- "age"是 数字
- "isStudent"是 布尔值
- "hobbies"是一个 字符串数组
- "address"是一个 嵌套的对象
- "nullValue"是 null
接下来的是困扰我多年来的疑惑,感谢DeepSeek
2.JavaScript对象字面量的写法
提问:下面这个语句,和json有什么关系?为什么name、age不用双引号括起来
let obj = { name: "张三", age: 30 }
解释:
它们是javascript对象字面量的写法
需要明确区分:JSON是一种数据格式,而JavaScript对象是JavaScript语言中的一种数据结构。
在JavaScript中,对象字面量的键可以不用引号(当键是有效的标识符时),也可以使用单引号或双引号。以下写法在JavaScript中都是合法的:
let obj1 = { name: "张三" };
let obj2 = { "name": "张三" };
let obj3 = { 'name': "张三" };
3.ES6 属性简写 (Property Shorthand)
当对象属性名与变量名相同时,可省略键值对中的值声明:
// 传统写法{ userName: userName, salary: salary } // ES6 简写(完全等效){ userName, salary }
所以在接下来的示例中,你会它们三个的混合,代码如下:
var userName = $("#username").val();
var salary = $("#salary").val();
//简化写法
var args=JSON.stringify({
userName,
salary
})
//完整写法
// var args=JSON.stringify({
// "userName":userName,
// "salary":salary
// })
本质上,进行了如下的转换:
JSON.stringify()
会自动处理为合法 JSON:
小结
综上,一个是json,一个是javascript,然后通过JSON字符串工具,进行了转换。格式上有相似的地方,注意区分。
处理JSON-@ResponseBody
说明
项目开发中,我们往往需要服务器返回的数据格式是按照json来返回的,我们看一下SpringMVC是如何处理的,项目效果示意图
1.请求一个json数据
2.响应json数据
应用案例
1.引入处理json需要的jar包,注意spring5.x需要使用jackson-2.9.x.jar的包。
2.创建web/json.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>请求json</title><%--引入jquery--%><%--编写jquery代码--%></head>
<body>
<h1>请求一个json数据</h1><a href="?" id="getJson">点击获取json数据</a>
</body>
</html>
3.创建com/stein/springMVC/json/entity/Dog.java
public class Dog {private String name;private int age;//有参,无参构造器,getter and setter,toString
}
4.com/stein/springMVC/json/handler/JsonHandler.java
@Controller
public class DogHandler {@RequestMapping("/json/dog")@ResponseBodypublic Dog getDog() {return new Dog("小花",3);}
}
5.修改web/json.jsp
1)完善href链接;
2)使用jquery 发送ajax请求
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>请求json</title><%--引入jquery--%><script type="text/javascript" src="script/jquery-3.6.0.min.js"></script><%--编写jquery代码--%><script type="text/javascript">$(function () {$("#getJson").click(function () {var url = this.href;var args = {"age":8,//每次发送的时间都是新的,避免浏览器使用缓存"time":new Date()}$.post(url,args,function (data) {console.log("data=",data)console.log("name=",data.name)console.log("age=",data.age)},"json")//避免页面跳转return false;})})</script></head>
<body>
<h1>请求一个json数据</h1><%--相对路径写法--%>
<%-- <a href="json/dog" id="getJson">点击获取json数据</a> <br>--%><%--绝对路径写法--%><a href="<%=request.getContextPath()%>/json/dog" id="getJson">点击获取json数据-by full path</a></body>
</html>
6.测试
使用Postman测试
处理JSON-@RequestBody
说明
- 前面是通过表单,或者url请求携带 参数名=参数值 把数据提交给目标方法
1)给大家举例客户端发送json字符串数据,
2)使用SpringMVC的@RequestBody将客户端提交的json数据,封装成JavaBean对象
3)再把这个javabean以json对象形式返回
4)完成效果示意图
应用案例
1.添加新的json请求,修改web/json.jsp
以前是通过表单form标签的形式提交的,现在是通过ajax来提交数据
username:<input type="text" id="username"/> <br>
salary: <input type="text" id="salary"/> <br>
<button name="submit" >提交</button>
2.创建com/stein/springMVC/json/entity/User.java
public class User {private String userName;private int salary;Constract,setter,getter,toString
3.添加方法,com/stein/springMVC/json/handler/JsonHandler.java
@Controller
@RequestMapping("/json")
public class JsonHandler {@RequestMapping("/dog")@ResponseBodypublic Dog getDog() {//System.out.println("age=" + age);return new Dog("小花",3);}@RequestMapping("/user")@ResponseBody //返回的数据是jsonpublic User saveUser(@RequestBody User user) {//接受的实参是json,然后转换成javabean对应的userSystem.out.println("user="+user);return user;}
}
4.web/json.jsp中编写ajax代码
<%--编写jquery代码--%><script type="text/javascript">$(function () {$("button[name=submit]").click(function () {var url="json/user";var userName = $("#username").val();var salary = $("#salary").val();//简化写法var args=JSON.stringify({userName,salary})//完整写法// var args=JSON.stringify({// "userName":userName,// "salary":salary// })$.ajax({url,data:args,type:"post",success:function (data,state,xhr) {console.log("data=",data)console.log("state=",state)console.log("xhr",xhr)},// 表示提交的数据是json格式的contentType:"application/json;charset=uft-8"})return false;})})</script>
5.测试
postman再次测试
注意事项和细节
1.目标方法正常返回JSON需要的数据,可以是一个对象,也可以是一个集合。区别于之前只返回一个Dog对象->转成Json数据格式返回
2.@ResponseBody可以直接写在controller上,这样对所有方法生效
3.@ResponseBody+@Controller可以直接写成@RestController,我们看一下源码!
梳理数据传输格式
1.客户端ajax的设置数据格式的两种方式
contentType:用于设置发送数据到服务器时所使用的内容类型
dataType: 设置预期服务器返回的数据类型
2.后端设置数据格式的方式
//设置请求的字符编码
request.setCharacterEncoding("uft-8");//设置响应的字符编码
response.setContentType("text/html;charset=utf-8");
3.混乱的来源
理解字面上,客户端发送请求的contentType和后端响应的contentType是同一个字段,然而一个用在请求上,一个用在响应上,给人使用不一致的感觉。
应当站在各方角度上来理解,客户端用contentType设置了前端发送的格式,后端用contentType设置了后端的发送格式,这儿后端面的发送就是响应Response。
总结规律:
-
数据发送方负责声明格式:
-
客户端发请求 → 用
contentType
-
服务器返响应 → 用
response.setContentType()
-
-
数据接收方负责配置解析方式:
-
服务器收请求 → 用
request.setCharacterEncoding()
-
客户端收响应 → 用
dataType
(可选) + 自动按Content-Type解析
-
HttpMessageConverter<T>
● 基本说明
SpringMVC处理JSON-底层实现是依靠HttpMessageConverter<T>来进行转换的
● 工作机制简图
●处理JSON-底层实现(HttpMessageConverter<T>)
1.使用HttpMessageConverter<T>将请求信息转化并绑定到处理方法的入参中,或将响应结果转为对应类型的响应信息,Spring提供了两种途径:
√使用@RequestBody/@ResponseBody对目标方法进行标注
√使用HttpEntity<T>/ResponseEntity<T>作为目标方法的入参或返回值
2.当控制器处理方法使用到@RequestBody,/@ResponseBody或
HttpEntity<T>/ResponseEntity<T>时,Spring首先根据请求头或响应头的Accept属性选择匹配的
HttpMessageConverter,进而根据参数类型或泛型类型的过滤得到匹配的HttpMessageConverter,若找不到可用的HttpMessageConverter将报错
Dubug源码-梳理一下
1.从请求报文 ->springMVC方向
先 ctrl+n 找到HttpMessageConverter接口
再在该接口上 Ctrl+Alt+B 找到实现类列表,找到AbstractJackson2HttpMessageConverter这个实现类,找到下图方法打断点
以debug方式运行项目,提交数据
1)请求报文->HttpInputMessage
此时运行到打断点的位置,对参数inputmessage估值可以看到,将请求报文封装到了HttpInputMessage中。headers是表头,body是传输的数据内容
2)HttpInputMessage -> HttpMessageConverter
通过对contentType的获取,得到要转换的类型
从而选择对应的转换器
3. HttpMessageConverter -> SpringMVC的javabean
从inputMessage的body中获取信息,封装到对应javaType中
下断点到JsonHandler的方法上
此时可以看到,数据已经成功封装到javabean上
2.springMVC->输出报文方向
1)springMVC->HttpMessageConverter
断点到writeInternal方法上,继续执行,可以执行到该处,此时,依然通过contentType获取要返回的类型
得到要进行转换的转换器
2)HttpMessageConverter->HttpOutputMessage的封装
3)回写报文