一、HttpMessageConverter

HttpMessageConverter:报文信息转换器,是 SpringMVC 的一个 接口(策略模式),用于:

  • 将请求体(request body)转换为 Java 对象(反序列化)

  • Java 对象转换为响应体(response body)(序列化)

简单理解它就是:处理请求/响应体的“翻译器”

场景 HttpMessageConverter 做的事 举例
请求时(客户端 → 服务端) JSON/XML/文本 → Java 对象(反序列化) 把 JSON 请求体转成 User 对象
响应时(服务端 → 客户端) 把 Java 对象 → JSON/XML/文本(序列化) List<User> 转为 JSON 响应

HttpMessageConverter提供了两个注解和两个类型:

@RequestBody,@ResponseBody,RequestEntity, ResponseEntity。

RequestEntity:整个请求实体,可以是:请求头+请求体

1-1、@RequestBody —— 请求体转 Java 对象

示例1:

@PostMapping("/user")
public String addUser(@RequestBody String requestBody) {
    // username=Tom&password=123456
    System.out.println("requestBody:"+requestBody);
    return "success";
}

【注意】:

1、此时的返回内容是:username=Tom&password=123456,但是若是形参直接用User对象,则会报错,应为:表单默认的提交方式是:application/x-www-form-urlencoded,而要从请求体中读取 JSON内容,需要提交方式是:'Content-Type': 'application/json'

2、虽然配置了<mvc:default-servlet-handler/>,使得项目可以直接访问静态资源html文件,但是,这个html页面是 直接被 Tomcat 的 DefaultServlet 加载的,并没有经过 SpringMVC,也就 不会处理 Thymeleaf 模板语法,比如:<form th:action="@{/testRequestBody}" method="post">,这里的 th:action 不会生效,所以点击提交后,form 的 action 根本没正确生效!导致请求根本就没发到你想要的 /testRequestBody,更别说进入 Controller 了!

静态页面 vs 模板页面路径区别

放置目录 类型 是否能渲染 Thymeleaf
/webapp/static//static/ 静态资源 ❌ 不会渲染 th:* 标签
/WEB-INF/templates/ 模板(推荐) ✅ 会渲染 Thymeleaf

示例2:

    @PostMapping(value = "/history/save")
    @ResponseBody
    public Result<?> saveHistory(@RequestBody ChatHistoryVO chatHistoryVO) {
        return chatService.saveHistory(chatHistoryVO);
    }

1-2、RequestEntity—— 封装请求体 + 请求头等信息

RequestEntity 封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过 getHeaders () 获取请求头信息,通过 getBody () 获取请求体信息。

@PostMapping("/data")
public String postData(RequestEntity<String> requestEntity) {
    System.out.println("请求体内容:" + requestEntity.getBody());
    System.out.println("请求头:" + requestEntity.getHeaders());
    return "ok";
}

1-3、通过servletAPI响应浏览器数据

    @RequestMapping("/testResponse")
    public void testResponse(HttpServletResponse response) throws IOException {
        response.getWriter().print("hello HttpServletResponse");
    }

此时,页面会打印内容!

响应的响应体就是我们在浏览器中能看到的内容

1-4、@ResponseBody —— Java 对象转响应体(直接输出)

示例1:返回文本数据

    @RequestMapping("/testResponseBody")
    @ResponseBody
    public String testResponseBody(){
        // 加上了@ResponseBody,这个返回的success123就是响应体
        // 去掉@ResponseBody,这个返回的success123才是视图名称!
        return "success123";
    }

返回内容:

示例2:返回对象信息

    @RequestMapping("/testResponseBody2")
    @ResponseBody
    public User testResponseBody2(){
        return new User("admin123", "123", "admin");
    }

【注意】:

此时,浏览器会报错,因为,浏览器并不知道我们的对象类型,所以,要转换成json格式,要导入jar包!

<!-- json依赖 -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.1</version>
</dependency>

返回信息:json格式的字符串!

本身浏览器能够接受的服务器的返回格式就是字符串!

在 SpringMVC 中,如果你用 @ResponseBody 注解将 Java 对象 作为响应返回:

  • ✔️ Spring 会自动使用 HttpMessageConverter(比如 MappingJackson2HttpMessageConverter)把这个 Java 对象转换成 JSON 字符串。

  • ✔️ 无论你用 表单提交AJAX 请求Axios 还是其他方式,只要浏览器收到了这个响应,它看到的就是 JSON 字符串。看得到但用不了

  • 但如果你想在浏览器中动态处理这个 JSON(比如展示到网页上某个 <div> 里),你确实需要用 JavaScript 的 AJAX / fetch / Axios 等方式来发起请求处理响应

1、springMVC处理JSON

在 SpringMVC 的核心配置文件中开启 mvc 的注解驱动,此时在 HandlerAdaptor 中会自动装配一个消息转换器:MappingJackson2HttpMessageConverter,可以将响应到浏览器的 Java 对象转换为 Json 格式的字符串

<mvc:annotation-driven />

在处理器方法上使用 @ResponseBody 注解进行标识

将 Java 对象直接作为控制器方法的返回值返回,就会自动转换为 Json 格式的字符串。(不是走视图解析器)

1-5、@RestController 注解

@RestController 注解是 springMVC 提供的一个复合注解,标识在控制器上,就相当于为类添加了 @Controller 注解,并且为其中的每个方法添加了 @ResponseBody 注解

因为@ResponseBody非常常用!

示例:

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

@RestController
public class UserController {

    // GET 请求:http://localhost:8080/user
    @GetMapping("/user")
    public User getUser() {
        return new User("Alice", 25);
    }

    // POST 请求:接收 JSON 参数并返回
    @PostMapping("/login")
    public String login(@RequestBody User user) {
        if ("admin".equals(user.getName()) && user.getAge() == 123) {
            return "登录成功";
        } else {
            return "用户名或密码错误";
        }
    }
}

1-6、ResponseEntity

1、什么是 ResponseEntity

 ResponseEntity用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文。

ResponseEntity<T> 是 Spring 提供的一个响应封装类,它可以让你:

  • 指定响应体(body);

  • 设置响应状态码(status);(可以是自定义状态码

  • 添加响应头(headers)。

一句话总结:

@ResponseBody 更高级的响应返回方式,更灵活、更完整。

2、为什么用 ResponseEntity

用法 是否能做到
返回一个对象作为 JSON @ResponseBody@RestController 都可以
返回 404、500 等状态码 @ResponseBody 不方便
自定义响应头(如 Content-Type) ❌ 不方便
完整控制 body + status + headers ResponseEntity 轻松实现

示例:

@PostMapping("/login")
public ResponseEntity<String> login(@RequestBody User user) {
    if ("admin".equals(user.getName()) && user.getAge() == 123) {
        return ResponseEntity.ok("登录成功"); // 200
    } else {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                             .body("用户名或密码错误"); // 401
    }
}

可以把 @RestControllerResponseEntity 搭配使用

3、文件的下载 

因为“文件下载需要设置响应头和状态码”,而 ResponseEntity 可以很方便做到这些,所以它在 文件下载场景中特别有用。 

文件的上传和下载,本质就是文件的复制(客户端、服务器)!

示例:

<h1>ResponseEntity 实现文件的下载</h1>
<a th:href="@{/fileDown}">001.jpg文件下载</a>
    @RequestMapping("/fileDown")
    public ResponseEntity<byte[]> fileDown(HttpSession session) throws IOException {
        // 获取servletContext对象
        ServletContext servletContext = session.getServletContext();
        // 获取服务器中文件的真实路径(当前项目部署到服务器中的真实路径)
        String realPath = servletContext.getRealPath("/static/img/001.jpg");
        System.out.println(realPath);
        // 创建输入流
        FileInputStream fileInputStream = new FileInputStream(realPath);
        // 以文件的整个字节创建byte[]
        byte[] bytes = new byte[fileInputStream.available()];
        // 将流读到字节数组中
        fileInputStream.read(bytes);

        // 创建httpHeaders对象设置响应头信息
        MultiValueMap<String, String> headers = new HttpHeaders();
        // 除了filename的值,其余的内容固定!
        // example.txt是下载的文件名
        headers.add("Content-Disposition", "attachment; filename=example.jpg");

        // 设置响应状态码
        HttpStatus status = HttpStatus.OK;
        ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(bytes, headers, status);

        // 关闭流
        fileInputStream.close();
        return responseEntity;
    }

【备注】:输入,输出流

流类型 方法 用途
输入流 FileInputStreamServletContext.getResourceAsStream() 从磁盘或资源中读取文件内容
输出流 HttpServletResponse.getOutputStream() 输出到客户端浏览器
站在内存的角度看,把资源读出来 / 写进去!

4、文件的上传:MultipartFile

上传时,通常用 MultipartFile 接收。

(1)、MultipartFile 是什么?

MultipartFile 是 Spring 提供的接口,用于处理前端上传的文件。

它代表 一个上传的文件,通常配合 <form enctype="multipart/form-data"> 使用。

在 Controller 中,只要你在方法参数里写上:

public String upload(@RequestParam("file") MultipartFile file)

SpringMVC 就会自动帮你把上传的文件封装成 MultipartFile 对象。 

(2)、常用方法:
方法 说明
getName() 获取字段名(表单中的 name)
getOriginalFilename() 获取上传时的原始文件名
getContentType() 获取文件的 MIME 类型(如 image/png)
getSize() 获取文件大小(单位:字节)
isEmpty() 是否上传了文件
getBytes() 获取文件内容,返回 byte[]
getInputStream() 获取文件内容的输入流
transferTo(File dest) 保存文件到目标位置

(3)、多文件上传:MultipartFile[]

@PostMapping("/uploadMultiple")
public String uploadMultiple(@RequestParam("files") MultipartFile[] files) {
    for (MultipartFile file : files) {
        if (!file.isEmpty()) {
            System.out.println("上传:" + file.getOriginalFilename());
            // file.transferTo(...);
        }
    }
    return "上传完成";
}

HTML 表单要这样写:

<form method="post" enctype="multipart/form-data" action="/uploadMultiple">
  <input type="file" name="files" multiple>
  <button type="submit">上传多个</button>
</form>

(4)、注意

问题 说明
前端表单中必须写 enctype="multipart/form-data" 否则后端收不到文件
后端方法参数必须写 @RequestParam("file") 参数名要和 input 的 name 匹配
文件太大上传失败 检查是否配置了上传文件大小限制,如 multipartResolver 设置
文件被覆盖 可自行重命名文件,比如:加时间戳、UUID

【完整的步骤】: 

步骤一:添加pom.xml依赖

<!-- 文件上传功能需要 Apache Commons FileUpload -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>

 步骤二:Spring 配置文件中启用 MultipartResolver(其实就是一个<bean>

<!-- 处理文件上传的 Bean -->
<bean id="multipartResolver"
      class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="5242880"/> <!-- 5MB -->
</bean>

【注意】:

id是固定的! 

步骤三:编写前端代码:

步骤四:后台代码(用@RequestParam获取上传的文件)

    @PostMapping("/fileUpload")
    public String fileUpload(@RequestParam("photo") MultipartFile photo, HttpSession session) throws IOException {
        String originalFilename = photo.getOriginalFilename();
        ServletContext servletContext = session.getServletContext();
        String realPath = servletContext.getRealPath("photo");
        File file = new File(realPath);
        if(!file.exists()){
            file.mkdir();
        }
        
        // 不知道当前系统的文件地址分隔符是什么斜线,可以用:File.separator
        String finalPath = realPath + File.separator + originalFilename;
        // 文件上传
        photo.transferTo(new File(finalPath));
        return "success";
    }

步骤五:使用UUID生成文件名

5、【补充】:servletContext.getRealPath

servletContext.getRealPath("xxxxx") 获取的是 Web 应用中,相对路径 "xxxxx" 对应的 绝对路径(在服务器文件系统中的路径)


举个例子:假设你的 Web 应用部署在服务器的这个路径下:

/Users/wangsi/javaCode/my-webapp/

你的项目结构大概是这样:

当你启动 Web 服务器(如 Tomcat)后,ServletContext.getRealPath("photo") 返回的路径 就是部署后实际文件系统路径,比如:

/Users/wangsi/javaCode/my-webapp/target/my-webapp/photo

(1)、关键点总结:

内容 含义
"photo" 相对于 Web 根目录的路径
getRealPath("photo") 返回 "photo" 文件夹在服务器硬盘上绝对路径
返回类型 String,如:/usr/local/tomcat/webapps/your-app/photo
前提 这个路径必须真实存在,且应用必须是以 exploded(解压)方式部署

(2)、注意事项:

  1. 打包成 WAR 部署到服务器时,如果是压缩包形式,getRealPath() 可能会返回 null

  2. getRealPath() 通常用于开发调试环境,不推荐用于生产环境中的文件访问逻辑。


如需在生产环境读取资源文件,推荐用这种方式:

InputStream in = servletContext.getResourceAsStream("/photo/test.jpg");

它能在 WAR 包中读取资源,而不依赖于实际文件路径

二、JSON回顾

JSON 是一种轻量级的数据交换格式,用于在不同系统(特别是前后端之间)传递结构化数据

它的格式来源于 JavaScript,但如今已被几乎所有编程语言支持(包括 Java、Python、R 等)。

XML也是数据交换格式,但是由于生成的数据量比json大,而且解析较为困难,导致,json用的更多,xml更多是作为配置文件使用!

2-1、JSON 的基本语法结构

JSON 数据结构只有两种:

  1. 对象(Object):键值对集合,用花括号 {} 表示。

  2. 数组(Array):值的有序集合,用方括号 [] 表示。

1. JSON 对象(类似 Java 的 Map<String, Object>):
{
  "name": "Alice",
  "age": 25,
  "isStudent": true
}
2. JSON 数组(类似 Java 的 List<Object>):
[
  "apple",
  "banana",
  "cherry"
]
3. 更复杂的嵌套结构:
{
  "person": {
    "name": "Bob",
    "contacts": ["bob@example.com", "123456"],
    "address": {
      "city": "Kuala Lumpur",
      "postcode": "50000"
    }
  }
}

2-2、JSON 的数据类型(只有这几种)

类型 示例
字符串 "hello"
数字 42, 3.14
布尔值 true, false
空值 null
对象(Object) { "key": "value" }
数组(Array) [1, 2, 3]

2-3、JSON 的常见用途

场景 说明
前后端数据通信 Web开发中,前端发送 JSON 给后端
配置文件 比如 .eslintrc.json, package.json
存储结构化数据(NoSQL) MongoDB 就使用 JSON-like 的 BSON 格式
API 响应格式(RESTful API) 大部分接口返回 JSON 响应

2-4、Java 处理 JSON 示例

常用库:Jackson / Gson / org.json

// 用 Jackson 将对象转 JSON 字符串
ObjectMapper mapper = new ObjectMapper();
User user = new User("Alice", 25);
String json = mapper.writeValueAsString(user);

// 反序列化 JSON 字符串为 Java 对象
User parsed = mapper.readValue(json, User.class);

2-5、Python 处理 JSON 示例

import json

# Python 对象转 JSON 字符串
data = {"name": "Alice", "age": 25}
json_str = json.dumps(data)

# JSON 字符串转 Python 对象
parsed_data = json.loads(json_str)

2-6、注意事项

  1. JSON 中 必须是双引号包裹的字符串(不能省略)

    {"name": "Tom"} ✅
    {name: "Tom"} ❌ 错误
    
  2. JSON 没有注释功能(不像 Java 里可以 // 注释

三、AJAX讲解

3-1、什么是 AJAX?

AJAX(Asynchronous JavaScript and XML)是前端技术的一种组合,可以让网页在不刷新整个页面的情况下,与服务器交换数据更新部分内容

它的核心目标是:实现异步请求

虽然名字里有 XML,但现在最常用的格式其实是 JSON


3-2、为什么要用 AJAX?

1、没有 AJAX 时的传统流程:

  1. 用户点击一个按钮;

  2. 整个页面发送请求到服务器;

  3. 服务器返回一个完整的新页面;

  4. 页面刷新,重新加载

2、用 AJAX 后:

  1. 用户点击按钮;

  2. 浏览器使用 JS 发送 AJAX 请求;

  3. 服务器返回数据(通常是 JSON);

  4. 用 JS 动态更新网页的一部分(不用刷新页面)!


3-3、AJAX 的核心组成

技术 说明
JavaScript 发起请求、处理响应
XMLHttpRequest 或 Fetch API 发起 HTTP 请求
HTML DOM 用于动态修改网页内容(不刷新)
JSON 最常用的数据格式(也可用 XML)
后端语言 处理请求返回数据(如 Java、Python)

3-4、前端库封装了 AJAX

库/框架 示例
jQuery $.get("/api", callback)
Axios axios.get("/api").then(res => {})
Vue/React fetch/axios 或封装好的 API

axios是目前前端最热门的ajax请求库!

示例:

<h1>springMVC handle ajax request</h1>
<div id="app">
    <a th:href="@{/testAxios}" @click="testAxios">springMVC handle ajax request</a>

    <div id="result"></div>
</div>
<!-- 引入 Vue -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<!-- 引入 axios  -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

<script>
    new Vue({
        el: '#app',
        methods: {
            testAxios: function (event) {
                // 取消超链接的默认行为
                event.preventDefault();
                axios({
                    methods: 'post',
                    url: event.target.href,
                    params: {
                        username:'admin123',
                        password:'123456'
                    }
                }).then(
                    function (response) {
                        //alert(response.data);
                        document.getElementById("result").innerText =
                            "role:" + response.data.role;
                    }
                )
            }
        }
    })
</script>
    @RequestMapping("/testAxios")
    @ResponseBody
    public User testAxios(String username, String password){
        System.out.println("username = " + username + ", password=" + password);
        return new User("admin123", "123", "admin");
    }
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐