【JavaEE12-后端部分】SpringMVC07-综合案例3-从留言板看前后端交互:接口文档与HTTP通信详解

1. 案例背景与需求

1.1 需求描述

实现一个留言板功能:

  • 页面加载时,获取所有留言并显示。
  • 用户填写"谁"、"对谁"、"说什么",点击提交,将留言发送到后端存储。
  • 后端存储成功后返回JSON结果,前端将新留言追加到页面,无需刷新。

1.2 为什么用JSON?

因为留言数据包含多个字段,且可能嵌套,JSON能完美表达。同时,前后端分离开发中,JSON是事实标准。

1.3 涉及接口

  • 发布留言:/message/publish (POST)
  • 获取留言列表:/message/getList (GET)

1.4 案例演示



2. 接口文档定义

2.1 发布留言接口

项目 内容
请求路径 /message/publish
请求方法 POST
接口描述 发布一条新留言
请求参数(JSON格式)
参数名 类型 是否必须 说明
msgFrom String 留言者
msgTo String 接收者
message String 内容

请求示例(JSON):

json 复制代码
{
    "msgFrom": "张三",
    "msgTo": "李四",
    "message": "你好"
}
响应数据
  • Content-Type : application/json;charset=UTF-8
  • 响应体 : JSON对象,包含 ok 字段,1表示成功,0表示失败

响应示例

json 复制代码
{"ok": 1}

2.2 获取留言列表接口

项目 内容
请求路径 /message/getList
请求方法 GET
接口描述 获取所有留言
请求参数

响应数据
  • Content-Type : application/json;charset=UTF-8
  • 响应体: 留言对象数组

响应示例

json 复制代码
[
    {"msgFrom":"张三","msgTo":"李四","message":"你好"},
    {"msgFrom":"王五","msgTo":"赵六","message":"hello"}
]

2.3 为什么这样定义?

  • 发布留言需要提交结构化数据,所以用JSON。
  • 获取留言返回数组,也适合用JSON。
  • 统一使用JSON,前后端解析方便。

3. 前端开发:基于接口文档实现

3.1 留言板页面(messageBoard.html)

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>留言板</title>
    <style>
       * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: "Microsoft YaHei", Arial, sans-serif;
        }

        body {
            background-color: #f5f7fa;
            padding: 20px 0;
        }

        .container {
            width: 380px; /* 适度加宽,提升视觉舒适度 */
            min-height: 400px; /* 最小高度,避免内容过少时页面塌陷 */
            margin: 0 auto;
            text-align: center;
            background-color: #ffffff; /* 白色背景,突出内容 */
            padding: 25px 20px; /* 内边距,让内容不贴边 */
            border-radius: 12px; /* 圆角,弱化硬朗感 */
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); /* 轻微阴影,提升层次感 */
        }

        .container h1 {
            color: #333;
            font-size: 24px;
            margin-bottom: 12px;
            font-weight: 600;
        }

        .grey {
            color: #909399; /* 更柔和的灰色 */
            font-size: 14px;
            margin-bottom: 20px;
            display: block; /* 独占一行,提升间距 */
        }

        .container .row {
            width: 100%; /* 改用百分比,适配容器宽度 */
            height: 50px; /* 适度加高,提升输入体验 */
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 15px; /* 行间距,避免拥挤 */
        }

        .container .row span {
            color: #606266;
            font-size: 15px;
            font-weight: 500;
            width: 60px; /* 固定标签宽度,对齐更整齐 */
            text-align: left;
        }

        .container .row input {
            width: calc(100% - 70px); /* 自适应宽度,适配容器 */
            height: 38px;
            padding: 0 12px; /* 内边距,输入文字不贴边 */
            border: 1px solid #e5e6eb; /* 浅灰色边框,更柔和 */
            border-radius: 6px; /* 圆角输入框 */
            font-size: 14px;
            outline: none; /* 清除默认外边框 */
            transition: border-color 0.2s ease; /* 过渡动画,提升交互感 */
        }

        /* 输入框聚焦样式,提升交互反馈 */
        .container .row input:focus {
            border-color: #ff9900; /* 与按钮同色系,视觉统一 */
            box-shadow: 0 0 0 2px rgba(255, 153, 0, 0.1); /* 轻微高光,突出聚焦状态 */
        }

        #submit {
            width: 100%; /* 适配容器宽度 */
            height: 45px;
            background-color: #ff9900; /* 更饱和的橙色,视觉更舒适 */
            color: white;
            border: none;
            margin: 15px 0 20px; /* 调整间距,上下更均衡 */
            border-radius: 8px;
            font-size: 16px;
            font-weight: 500;
            cursor: pointer; /* 鼠标悬浮显示手型 */
            transition: background-color 0.2s ease; /* 过渡动画 */
        }

        /* 按钮悬浮效果,提升交互体验 */
        #submit:hover {
            background-color: #e68a00; /* 稍深的橙色,反馈悬浮状态 */
        }

        /* 留言列表样式优化 */
        .container > div:not(.row) {
            padding: 12px 15px;
            background-color: #fafafa;
            border-radius: 6px;
            text-align: left;
            color: #333;
            font-size: 14px;
            margin-bottom: 10px;
            border-left: 3px solid #ff9900; /* 左侧小色块,区分留言项 */
            word-wrap: break-word; /* 自动换行,避免文字溢出 */
        }
    </style>
</head>

<body>
    <div class="container">
        <h1>留言板</h1>
        <p class="grey">输入后点击提交, 会将信息显示下方空白处</p>
        <div class="row">
            <span>谁:</span> <input type="text" name="" id="from">
        </div>
        <div class="row">
            <span>对谁:</span> <input type="text" name="" id="to">
        </div>
        <div class="row">
            <span>说什么:</span> <input type="text" name="" id="say">
        </div>
        <input type="button" value="提交" id="submit" onclick="submitMessage()">
        <!-- <div>A 对 B 说: hello</div> -->
    </div>

    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
    <script>
        //我们在页面解析之前,需要将留言板的内容给获取到
        $.ajax({
            method: "get",
            url: "message/getList",
            success: function(message) {//返回的是JSON对象
                var info = "";
                //由于你后端返回的是JSON对象,那么我的前端就可以直接当做对象来使用
                if(message !== null && message.length>0){
                    for(var msg of message) {
                        info += "<div>" + msg.msgFrom + "对" + msg.msgTo + "说:" + msg.message + "</div>";
                    }
                    //最后追加到子标签中
                    $(".container").append(info);
                }
                
            }
        })

        function submitMessage(){
            //当点击提交的时候,我们需要请求后端,然后将东西提交到后台

            //此时需要将你提交的数据给提取出来,然后还要进行序列化
            //即需要将对象变为JSON字符串

            //1. 获取留言的内容
            var from = $('#from').val();
            var to = $('#to').val();
            var say = $('#say').val();
            if (from== '' || to == '' || say == '') {
                return;
            }
            $.ajax({
                method: "post",
                url: "message/publish",
                contentType: "application/json",//你前端提交的是JSON,那么你就得指定这个东西,否则后端是不认识的
                data: JSON.stringify({//注意,这个东西是一个JSON,他不是对象,网络中无法直接提交对象
                    msgFrom: from,
                    msgTo: to,
                    message: say
                }),
                success: function(result) {
                    //如果你的后端返回的是JSON对象,那么前端是可以直接当做对象来使用的
                    console.log(result.ok);
                    if(result.ok == 1){//成功【注意如果后端返回的不是JSON对象,那么此时是不可以当做对象来点的】
                        //2. 构造节点
                        var divE = "<div>"+from +"对" + to + "说:" + say+"</div>";
                        //3. 把节点添加到页面上    
                        $(".container").append(divE);

                        //4. 清空输入框的值
                        $('#from').val("");
                        $('#to').val("");
                        $('#say').val("");
                    }else{//失败
                        alert("留言发布失败");
                    }
                }
            });
        }
        
    </script>
</body>

</html>

3.2 关键点解释

  • 获取留言列表:GET请求,无需参数,响应是JSON数组,jQuery自动解析为JavaScript数组,遍历生成HTML。
  • 发布留言
    • contentType: "application/json":必须设置,因为后端期待JSON。
    • data: JSON.stringify({...}):将JavaScript对象转为JSON字符串,因为HTTP只能传输字符串。
    • success 中的 result:由于后端返回JSON对象(响应头 application/json),jQuery自动解析为JavaScript对象,所以可以用 result.ok
  • 注意 :如果后端返回的是 text/plain,即使内容是 {"ok":1},jQuery也不会自动解析,result 就是字符串,无法用点语法。

4. 后端开发:根据接口文档实现

4.1 实体类 Message

java 复制代码
package com.zhongge.demo;

/**
 * @ClassName Message
 * @Description TODO 留言板信息实体类
 * @Author 笨忠
 * @Date 2026-02-22 18:20
 * @Version 1.0
 */
public class Message {
    private String msgFrom;//发送人
    private String msgTo;//接受人
    private String message;//所说的信息

    public String getMsgFrom() {
        return msgFrom;
    }

    public void setMsgFrom(String msgFrom) {
        this.msgFrom = msgFrom;
    }

    public String getMsgTo() {
        return msgTo;
    }

    public void setMsgTo(String msgTo) {
        this.msgTo = msgTo;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

4.2 Controller代码

java 复制代码
package com.zhongge.demo;

import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName MessageBoardController
 * @Description TODO 留言板控制器类
 * @Author 笨忠
 * @Date 2026-02-22 20:34
 * @Version 1.0
 */
@RequestMapping("/message")
@RestController//等价于 @Controller+@Responsebody
public class MessageBoardController {
    //这里我们还没有学习到数据库相关的东西,因此我们就先将数据存在内存中
    List<Message> list = new ArrayList<>();//使用list集合将我们的留言信息存在内存中


    /**
     * 发布留言
     * @param message 留言对象【谁,对谁,说什么】
     * @return 0-失败  1-成功
     */
    @PostMapping(value = "/publish", produces = "application/json")
    public String publish(@RequestBody Message message) {
        //首先就是验证参数
        //如果有其中一个是空的,那么我就觉得是不成功,此时就返回:0
        if (!StringUtils.hasLength(message.getMsgFrom())
            || !StringUtils.hasLength(message.getMsgTo())
            || !StringUtils.hasLength(message.getMessage())){
            return "{\"ok\": 0}";//自己构造一个JSON
        }
        //将数据存储在集合中
        list.add(message);
        //然后如果查询走到这里就说明成功,那么此时我就讲数据构造成JSON返回
        return "{\"ok\": 1}";
    }

    /**
     * 返回留言信息
     * @return 留言信息【集合】
     */
    @GetMapping("/getList")
    public List<Message> getMessageList() {
        return list;
    }
}

4.3 关键点解释

  • @RestController:所有方法返回值直接写入响应体。
  • @PostMapping(value = "/publish", produces = "application/json")
    • produces = "application/json" 告诉Spring这个方法返回的响应内容类型是JSON,这样即使返回String,Spring也会尽量设置响应头为 application/json(使用能处理String且支持JSON的转换器,如 MappingJackson2HttpMessageConverter)。
  • @RequestBody Message message:必须加,因为前端发的是JSON,需要从请求体读取并反序列化。
  • 返回值 String :手动构造JSON字符串。注意:如果去掉 produces,响应头将是 text/plain,前端就无法自动解析为对象。
  • getMessageList 返回 List<Message> :Spring自动用JSON转换器序列化,响应头 application/json

4.4 发布留言的处理流程

  1. 请求到达,Tomcat解析请求头,Content-Type: application/json,请求体是JSON字符串。

  2. SpringMVC找到 publish 方法,@RequestBody 触发消息转换器,将JSON反序列化为 Message 对象。

  3. 业务处理,存储到list,返回JSON字符串。

  4. 由于指定了 produces="application/json",Spring使用支持JSON的转换器写入响应,设置响应头 application/json

  5. 响应报文:

4.5 获取留言列表的处理流程

  1. GET请求,无请求体,Tomcat解析后交给Spring。

  2. 方法返回 list,Spring使用 MappingJackson2HttpMessageConverter 将list转为JSON数组,响应头 application/json

  3. 响应报文:


5. 为什么发布接口必须加 produces

因为返回的是 String,如果不加 produces,Spring默认用 StringHttpMessageConverter,响应头为 text/plain,前端无法自动解析为对象。加上 produces="application/json" 后,Spring会尝试使用能处理 application/json 的转换器(如 MappingJackson2HttpMessageConverter)来写String,从而设置正确的响应头。

更好的做法是返回一个 Map 或自定义对象,让Spring自动转JSON,这样就不需要 produces 了。例如:

java 复制代码
@PostMapping("/publish")
public Map<String, Integer> publish(@RequestBody Message message) {
    Map<String, Integer> result = new HashMap<>();
    if (校验通过) {
        list.add(message);
        result.put("ok", 1);
    } else {
        result.put("ok", 0);
    }
    return result;
}

这样返回的是对象,Spring自动用JSON转换器,响应头正确。


6. 深入理解序列化与反序列化

  • 序列化 :JavaScript对象 → JSON字符串(前端用 JSON.stringify),Java对象 → JSON字符串(后端用Jackson)。
  • 反序列化 :JSON字符串 → JavaScript对象(前端用 JSON.parse 或jQuery自动),JSON字符串 → Java对象(后端用 @RequestBody + Jackson)。

HTTP传输的始终是字节流,但通过 Content-Type 约定了解读方式。


7. 接口文档的作用再升华

  • 明确了请求和响应的格式,本例中发布接口请求体是JSON,响应也是JSON。
  • 规定了是否需要对数据进行序列化/反序列化
  • 避免了前后端因格式不匹配导致的bug

8. 常见问题

Q1:为什么获取留言列表不需要 produces

因为返回的是 List,Spring默认用JSON转换器,自动设置响应头为 application/json

Q2:如果发布接口不加 @RequestBody 会怎样?

后端会尝试从请求参数中取值,但请求体是JSON,不是表单格式,所以参数Map为空,message 对象的所有属性都是null,导致校验失败。

Q3:为什么前端要设置 contentType: "application/json"

如果不设置,jQuery默认用 application/x-www-form-urlencoded,请求体就会变成 msgFrom=张三&msgTo=李四&message=你好,后端用 @RequestBody 无法解析,因为这不是JSON。

Q4:如果后端返回的JSON字符串不是 {"ok":1} 而是 {"ok":1} 但响应头是 text/plain,前端还能用 result.ok 吗?

不能。因为响应头是 text/plain,jQuery不会自动解析,result 是字符串,没有 ok 属性。除非手动调用 JSON.parse(result)


9. 详细解释整个流程中 HTTP 是如何通信的

具体的流程如下简图所示:


图中的流程如下:


场景描述

我们有一个留言板页面(messageBoard.html),页面加载时会自动获取已有的留言列表并显示。用户填写"谁"、"对谁"、"说什么"后点击提交,新留言会发送到后端,后端存储后返回成功信息,前端将新留言追加到页面。


第一章:页面加载时的 GET 请求

1.1 浏览器加载页面

我们在浏览器中输入 http://localhost:8080/messageBoard.html,浏览器向服务器请求这个 HTML 文件。服务器返回 HTML,浏览器解析 HTML,遇到 <script> 标签加载 jQuery,然后执行其中的 JavaScript 代码。

1.2 执行 $.ajax GET 请求
javascript 复制代码
$.ajax({
    method: "get",
    url: "message/getList",
    success: function(message) {
        // 处理返回的数据
    }
});
  • method: "get":表示这是一个 GET 请求。
  • url: "message/getList":相对于当前页面的路径,假设当前页面在根路径,则完整 URL 为 http://localhost:8080/message/getList
  • 没有设置 contentType,因为 GET 请求通常没有请求体,不需要 Content-Type。
1.3 浏览器构造 HTTP 请求

浏览器构造的请求报文如下:

  • 请求行:GET /message/getList HTTP/1.1
  • 请求头:Host 表示服务器地址,Accept: */* 表示浏览器可以接受任何类型的响应。
  • 请求体:空(GET 没有请求体)。
1.4 服务器接收请求

请求到达服务器(Tomcat),Tomcat 解析请求行和请求头,然后根据 URL /message/getList 将请求交给 SpringMVC 的 DispatcherServlet

1.5 SpringMVC 找到对应方法

DispatcherServlet 根据 @GetMapping("/getList") 找到 MessageBoardController 中的 getMessageList 方法。

java 复制代码
@GetMapping("/getList")
public List<Message> getMessageList() {
    return list;
}
1.6 处理返回值和响应
  • 方法返回 List<Message> 对象。
  • 由于类上有 @RestController,Spring 知道要将返回值直接写入响应体(而不是解析为视图)。
  • Spring 遍历 HttpMessageConverter 列表,找到能处理 List<Message> 并支持客户端期望格式的转换器。这里客户端请求头 Accept: */*,表示接受任何格式,所以 Spring 会选择默认的 JSON 转换器 MappingJackson2HttpMessageConverter(因为项目中有 Jackson 依赖)。
  • 转换器将 list 对象序列化成 JSON 字符串,例如 [{"msgFrom":"张三","msgTo":"李四","message":"你好"}]
  • 同时,Spring 设置响应头 Content-Type: application/json;charset=UTF-8
  • Tomcat 将响应报文发送回浏览器。
1.7 浏览器接收响应

浏览器收到响应报文:

jQuery 的 AJAX 回调中,success: function(message) 被调用。由于响应头是 application/json,jQuery 自动将响应体 JSON 字符串解析为 JavaScript 对象数组,赋值给 message 参数。

1.8 前端处理数据
javascript 复制代码
if (message !== null && message.length > 0) {
    for (var msg of message) {
        info += "<div>" + msg.msgFrom + "对" + msg.msgTo + "说:" + msg.message + "</div>";
    }
    $(".container").append(info);
}

这里 msg 就是一个个 JavaScript 对象,可以直接用 msg.msgFrom 访问属性,因为它是解析后的对象。

关键点 :GET 请求不需要前端设置 Content-Type,但后端返回的响应头 Content-Type: application/json 让前端正确解析为对象。


第二章:提交留言的 POST 请求

2.1 用户输入数据

用户在三个输入框中输入:

  • 谁:张三
  • 对谁:李四
  • 说什么:你好

点击提交按钮,触发 submitMessage() 函数。

2.2 前端获取输入值并构造对象
javascript 复制代码
var from = $('#from').val(); // "张三"
var to = $('#to').val();     // "李四"
var say = $('#say').val();   // "你好"

// 构造 JavaScript 对象
var dataObj = {
    msgFrom: from,
    msgTo: to,
    message: say
};

此时 dataObj 是内存中的一个对象。

2.3 为什么不能直接发送 dataObj

因为 HTTP 协议只能传输字节流 ,不能传输内存对象。JavaScript 对象是 JS 引擎特有的数据结构,包含原型、方法等,其他语言(如 Java)无法理解。必须将对象转换成一种通用的、与语言无关的格式,这个过程叫序列化

2.4 序列化为 JSON 字符串
javascript 复制代码
JSON.stringify(dataObj)

得到 JSON 字符串:

json 复制代码
{"msgFrom":"张三","msgTo":"李四","message":"你好"}

这是一个纯文本,可以被编码成字节流发送。

2.5 设置 Content-Type

仅仅有 JSON 字符串还不够,服务器收到字节流后,需要知道如何解读它。所以要在请求头中加上 Content-Type: application/json,告诉服务器:"这是 JSON 格式的数据"。

javascript 复制代码
contentType: "application/json"
2.6 jQuery 构造 HTTP 请求

jQuery 将构造如下请求报文:

  • 请求行:POST /message/publish HTTP/1.1
  • 请求头:Content-Type 指明了请求体格式。
  • 请求体:JSON 字符串,被编码为 UTF-8 字节。
2.7 服务器接收请求

Tomcat 接收字节流,解析出请求行、请求头和请求体,封装成 HttpServletRequest 对象,交给 DispatcherServlet

2.8 SpringMVC 找到方法

DispatcherServlet 根据 URL /message/publish 和 POST 方法找到 publish 方法:

java 复制代码
@PostMapping(value = "/publish", produces = "application/json")
public String publish(@RequestBody Message message) { ... }
2.9 @RequestBody 的作用

@RequestBody 注解告诉 Spring:这个参数的值要从 HTTP 请求体中获取,并且要根据请求头的 Content-Type 选择合适的消息转换器来反序列化

2.10 解析请求体的过程
  1. Spring 从 HttpServletRequest 中获取 Content-Type 头,得到 application/json; charset=UTF-8
  2. 遍历所有注册的 HttpMessageConverter,找到能处理 application/json 且能读取 Message 类型的转换器。这里是 MappingJackson2HttpMessageConverter
  3. 转换器从请求输入流中读取所有字节,根据字符编码(UTF-8)解码成字符串:{"msgFrom":"张三","msgTo":"李四","message":"你好"}
  4. 使用 Jackson 库将 JSON 字符串反序列化为 Message 对象。Jackson 通过反射创建 Message 实例,然后根据 JSON 中的键名(msgFrommsgTomessage)调用对应的 setter 方法或直接赋值字段。
  5. 返回 Message 对象,赋值给方法参数 message

此时,Java 内存中有了一个 Message 对象,其属性值与前端输入一致。

2.11 业务处理

方法中先进行参数校验,然后执行:

java 复制代码
list.add(message);  // 将留言添加到内存列表
return "{\"ok\": 1}";

这里返回的是一个 Java 字符串,内容是 {"ok": 1}

2.12 返回响应时的关键点

方法返回后,Spring 需要将返回值写入响应体。由于类上有 @RestController,返回值会通过 HttpMessageConverter 处理。

问题 :返回值类型是 String,Spring 默认使用 StringHttpMessageConverter。这个转换器默认支持的媒体类型是 text/plain*/*,所以它会设置响应头 Content-Type: text/plain;charset=UTF-8,并将字符串原样写入响应体。

但是 ,我们在 @PostMapping 上加了 produces = "application/json"。这个属性告诉 Spring:这个方法生产(返回)的内容类型是 application/json 。Spring 在处理返回值时,会优先考虑能支持 application/json 的转换器。

对于 String 类型,MappingJackson2HttpMessageConverter 也能处理(它会将字符串当作 JSON 值写入),并且它支持 application/json。所以 Spring 会选择 MappingJackson2HttpMessageConverter,设置响应头 Content-Type: application/json;charset=UTF-8,响应体仍是字符串 {"ok": 1}

如果没有 produces,响应头就是 text/plain

2.13 响应报文(有 produces
2.14 前端接收响应

jQuery 收到响应后,检查响应头 Content-Type: application/json,于是自动调用 JSON.parse() 将响应体字符串解析为 JavaScript 对象,赋值给 result 参数。所以 result 是一个对象 { ok: 1 }

2.15 前端处理响应
javascript 复制代码
if (result.ok == 1) {
    // 构造新留言的 HTML 并追加
    var divE = "<div>" + from + "对" + to + "说:" + say + "</div>";
    $(".container").append(divE);
    // 清空输入框
    $('#from').val("");
    $('#to').val("");
    $('#say').val("");
} else {
    alert("留言发布失败");
}

因为 result 是对象,所以 result.ok 能正确取到值 1,条件成立,新留言被添加到页面。

2.16 如果没有 produces,会发生什么?

响应头会是 text/plain,jQuery 不会自动解析 JSON,result 就是一个字符串 "{\"ok\": 1}"。字符串是没有 ok 属性的,所以 result.okundefinedundefined == 1 为 false,程序会进入 else 分支,弹出"留言发布失败"。这就是为什么加上 produces 后前端才能看懂的原因。


第三章:为什么 getList 不用 produces 也能返回 JSON?【初学者的疑惑点

因为 getMessageList 返回的是 List<Message>,是一个 Java 对象(非 String)。Spring 在处理对象返回值时,默认会使用 MappingJackson2HttpMessageConverter,自动设置响应头为 application/json。所以不需要额外指定 produces

publish 返回的是 String,Spring 默认用 StringHttpMessageConverter,所以需要 produces 来改变默认行为。

更好的做法是让 publish 也返回一个 Java 对象,比如 Map<String, Integer>,这样 Spring 就会自动转 JSON,无需 produces


第四章:总结------每一步都清晰

4.1 前端到后端的完整流程
  1. 用户输入 → 内存中的 JavaScript 对象。
  2. 序列化JSON.stringify() 将对象转为 JSON 字符串。
  3. 设置 Content-TypecontentType: "application/json" 告诉服务器这是 JSON。
  4. 发送 HTTP 请求 :请求体为 JSON 字符串,请求头包含 Content-Type
  5. 服务器接收:Tomcat 解析请求,SpringMVC 找到方法。
  6. @RequestBody 触发 :根据 Content-Type 选择 JSON 转换器,将请求体反序列化为 Java 对象。
  7. 业务处理:操作数据,准备返回结果。
  8. 返回响应
    • 返回对象(如 List、Map)→ 自动 JSON,响应头 application/json
    • 返回 String → 默认 text/plain,除非用 produces 指定。
  9. 前端接收 :根据响应头 Content-Type 解析响应体,得到 JavaScript 对象或字符串。
4.2 核心概念再强调
  • 序列化:将内存对象转换为可传输的字节序列(如 JSON 字符串)。
  • 反序列化:将字节序列还原为内存对象。
  • Content-Type 请求头:告诉服务器请求体的格式。
  • Content-Type 响应头:告诉客户端响应体的格式。
  • @RequestBody:告诉 Spring 从请求体读取数据,并使用消息转换器反序列化。
  • produces:告诉 Spring 方法返回的媒体类型,影响响应头的设置。

第五章:我们可能还有的疑问

5.1 为什么 GET 请求不需要设置 Content-Type?

因为 GET 请求没有请求体,所以不需要描述体格式。Content-Type 只用于描述请求体

5.2 为什么后端返回 String 时,不加 produces 就是 text/plain?

因为 StringHttpMessageConverter 是专门处理字符串的,它认为字符串就是普通文本,所以默认设置为 text/plain。它不知道你的字符串内容是 JSON 格式。

5.3 如果我把返回的字符串改成 "ok"(不加花括号),前端会怎么样?

如果响应头是 text/plain,前端得到字符串 "ok"result 就是 "ok",无法用 result.ok 访问。如果响应头是 application/json,jQuery 会尝试解析 "ok" 为 JSON,但 "ok" 不是合法的 JSON(合法 JSON 需要是对象、数组或基本类型字符串,但 "ok" 本身作为字符串是合法的 JSON 文本,但 jQuery 解析后得到字符串 "ok",仍然无法用点语法)。所以返回格式要前后端约定一致。


10. 总结

通过三个案例,我们完整地展示了从接口文档定义到前后端实现的全过程,并深入解析了HTTP通信的每个细节。此时我们大概学会了:

  • 接口文档是前后端协作的基石。
  • HTTP传输的是字节流,需要靠 Content-Type 来解读。
  • 前端需要根据接口文档选择合适的格式并设置正确的请求头。
  • 后端需要根据接口文档使用合适的注解来接收数据,并返回正确的响应头。
  • 序列化和反序列化是网络通信的核心。

最后或许我们网络通信大家还有一点迷糊,于是呢我去向AI查了一些相关的资料:彻底搞懂 HTTP 通信:从字节流到对象,前后端数据交换全解析

最后的最后老铁们别忘了点赞👍、关注、加收藏哦👋~

相关推荐
minh_coo1 小时前
Spring单元测试之反射利器:ReflectionTestUtils
java·后端·spring·单元测试·intellij-idea
圆师傅1 小时前
Spring Boot中的日志log原理与自定义日志格式
spring boot·后端·logging
野生技术架构师1 小时前
Spring Boot + JPackage:构建独立安装包!
java·spring boot·后端
弹简特1 小时前
【JavaEE11-后端部分】SpringMVC06-综合案例2-从用户登录看前后端交互:接口文档与HTTP通信详解
java·spring boot·spring·http·java-ee·tomcat
xing-xing2 小时前
Spring Data Elasticsearch
后端·spring·elasticsearch
若光6722 小时前
springboot防抖 限流 幂等实现 AOP注解实现
java·spring boot·后端
人机与认知实验室2 小时前
交互论:交论与互论
交互
未来之窗软件服务2 小时前
AI人工智能(十五)C# AI的智障行为http服务—东方仙盟练气期
开发语言·http·c#
W|J2 小时前
websocket 的创建使用
websocket·网络协议