一、HttpMessageConverter

HttpMessageConverter是Spring MVC中非常重要的一个接口。翻译为:HTTP消息转换器,转换的是HTTP协议与Java程序中的对象之间的互相转换

1.1 HttpMessageConverter

该接口下提供了很多实现类,不同的实现类有不同的转换方式。

分类 常见实现类 核心功能与特点
处理JSON MappingJackson2HttpMessageConverter “翻译官中的主力”——它专门负责把后端的 Java 对象“翻译”成前端能看懂的 JSON 格式,或者把前端发来的 JSON 请求“翻译”回 Java 对象。就像你和一个说不同语言的人交流,它充当了同声传译的角色。
处理字符串 StringHttpMessageConverter “快递小哥”——它只送一种东西:纯文本。不管是 text/html、text/plain 还是其他文本格式,它只管把内容原封不动地递过去,不拆箱、不加工,简单直接。
处理表单 FormHttpMessageConverter “分拣员”——它专门处理 HTML 表单提交的数据(比如 username=张三&age=20 这种格式)。它会把这一长串“乱糟糟”的键值对拆开,分门别类地放进一个叫 MultiValueMap 的“收纳盒”里,方便后端取用。

请添加图片描述

如上图所示:HttpMessageConverter接口的可以将请求协议转换成Java对象,也可以把Java对象转换为响应协议。
HttpMessageConverter是接口,SpringMVC帮我们提供了非常多而丰富的实现类。每个实现类都有自己不同的转换风格。对于我们程序员来说,Spring MVC已经帮助我们写好了,我们只需要在不同的业务场景下,选择合适的HTTP消息转换器即可。
怎么选择呢?当然是通过SpringMVC为我们提供的注解,我们通过使用不同的注解来启用不同的消息转换器

1.2 @ResponseBody

@ResponseBody 是 Spring MVC 中的一个注解,它的作用是告诉 Spring:“方法的返回值不要进行视图解析,直接作为 HTTP 响应的内容体(response body)返回给客户端。”

@Controller
public class HelloController {

    @RequestMapping(value = "/hello")
    @ResponseBody
    public String hello(){
        return "{\"username\":\"zhangsan\",\"password\":\"1234\"}";
    }
}

在使用@ResponseBody注解之后,这里的"{“username”:“zhangsan”,“password”:“1234”}"不是逻辑视图名称了,而是作为响应体的内容进行响应。直接输出到浏览器客户端。以上程序中使用的消息转换器是:StringHttpMessageConverter。
同样我们可以将POJO对象以JSON格式的字符串响应给浏览器
第一步:引入jackson依赖,可以将java对象转换为json格式字符串

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>

  <artifactId>jackson-databind</artifactId>

  <version>2.17.0</version>

</dependency>

第二步:开启注解驱动<mvc:annotation-driven/>
第三步:控制器方法使用 @ResponseBody 注解标注(非常重要),控制器方法返回这个POJO对象

@Controller
public class HelloController {

    @RequestMapping(value = "/hello")
    @ResponseBody
    public User hello(){
        User user = new User("zhangsan", "22222");
        return user;
    }
}

1.3 @RestController

@RestController。这一个注解代表了:@Controller + @ResponseBody。

@RestController
public class HelloController {

    @RequestMapping(value = "/hello")
    public User hello(){
        User user = new User("zhangsan", "22222");
        return user;
    }
}

1.4 @RequestBody

@RequestBody作用请把 HTTP 请求体(request body)中的内容,完整地交给spring,并帮我转换成spring想要的 Java 类型
在没有使用这个注解的时候:

@RequestMapping("/save")
public String save(User user){
    // 执行保存的业务逻辑
    userDao.save(user);
    // 保存成功跳转到成功页面
    return "success";
}

处理流程:
Spring MVC 收到请求:username=zhangsan&password=1234
不经过 HttpMessageConverter
Spring 创建空的 User 对象
通过 WebDataBinder 遍历所有请求参数
找到 User 类中有 setUsername()、setPassword() 方法
调用这些 setter 方法,把参数值赋进去
给你一个组装好的 User 对象
不加 @RequestBody 时,Spring MVC 也能创建 Java 对象,但那是另一套完全不同的机制——数据绑定,并不是 @RequestBody 的作用。

使用这个注解的时候:

@RequestMapping("/save")
public String save(@RequestBody String requestBodyStr){
    System.out.println("请求体:" + requestBodyStr);
    return "success";
}

处理流程:
Spring MVC 看到 @RequestBody 注解
启动消息转换器机制,寻找能处理请求体的转换器
根据请求头 Content-Type: application/x-www-form-urlencoded
找到 FormHttpMessageConverter
FormHttpMessageConverter 读取整个请求体,作为原始字符串返回
把这个字符串传给你的 requestBodyStr 参数
当然他也可以将JSON字符串转换成POJO对象:
第一步:引入jackson依赖
第二步:开启注解驱动
第三步:创建POJO类,将POJO类作为控制器方法的参数,并使用 @RequestBody 注解标注该参数

@RequestMapping("/send")
@ResponseBody
public String send(@RequestBody User user){
    System.out.println(user);
    System.out.println(user.getUsername());
    System.out.println(user.getPassword());
    return "success";
}

1.5 RequestEntity

RequestEntity不是一个注解,是一个普通的类。这个类的实例封装了整个请求协议:包括请求行、请求头、请求体所有信息。RequestEntity 的核心组成:

public class RequestEntity<T> {
    private HttpMethod method;       // 请求方法(GET/POST等)
    private URI url;                 // 请求地址
    private HttpHeaders headers;     // 请求头
    private T body;                  // 请求体(泛型,可以是任何类型)
    // ...
}

出现在控制器方法的参数上:

@RequestMapping("/send")
@ResponseBody
public String send(RequestEntity<User> requestEntity){
    System.out.println("请求方式:" + requestEntity.getMethod());
    System.out.println("请求URL:" + requestEntity.getUrl());
    HttpHeaders headers = requestEntity.getHeaders();
    System.out.println("请求的内容类型:" + headers.getContentType());
    System.out.println("请求头:" + headers);

    User user = requestEntity.getBody();
    System.out.println(user);
    System.out.println(user.getUsername());
    System.out.println(user.getPassword());
    return "success";
}

1.6 ResponseEntity

ResponseEntity不是注解,是一个类。用该类的实例可以封装响应协议,包括:状态行、响应头、响应体。也就是说:如果你想定制属于自己的响应协议,可以使用该类。ResponseEntity 的核心组成:

public class ResponseEntity<T> {
    private final HttpStatus statusCode;  // 响应状态码(如 200 OK)
    private final HttpHeaders headers;    // 响应头
    private final T body;                 // 响应体(泛型,可以是任何类型)
    // ...
}

使用 ResponseEntity:完全控制响应:

@GetMapping("/user/{id}")
public ResponseEntity<User> getUser(@PathVariable Integer id) {
    User user = userService.findById(id);
    
    if (user == null) {
        // 可以返回404状态码
        return ResponseEntity.notFound().build();
    }
    
    // 可以自定义响应头和状态码
    return ResponseEntity.ok()
            .header("Custom-Header", "custom-value")
            .body(user);
}

二、文件上传与下载

1.1 文件上传

前端页面:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>文件上传</title>

</head>

<body>

<!--文件上传表单-->
<form th:action="@{/file/up}" method="post" enctype="multipart/form-data">
    文件:<input type="file" name="fileName"/><br>
    <input type="submit" value="上传">
</form>

</body>

</html>

web.xml文件:

<!--前端控制器-->
<servlet>
  
    <multipart-config>
        <!--设置单个支持最大文件的大小-->
        <max-file-size>102400</max-file-size>

        <!--设置整个表单所有文件上传的最大值-->
        <max-request-size>102400</max-request-size>

        <!--设置最小上传文件大小-->
        <file-size-threshold>0</file-size-threshold>

    </multipart-config>

</servlet>

Controller中的代码:

@Controller
public class FileController {

    @RequestMapping(value = "/file/up", method = RequestMethod.POST)
    public String fileUp(@RequestParam("fileName") MultipartFile multipartFile, HttpServletRequest request) throws IOException {
        String name = multipartFile.getName();
        System.out.println(name);
        // 获取文件名
        String originalFilename = multipartFile.getOriginalFilename();
        System.out.println(originalFilename);
        // 将文件存储到服务器中
        // 获取输入流
        InputStream in = multipartFile.getInputStream();
        // 获取上传之后的存放目录
        File file = new File(request.getServletContext().getRealPath("/upload"));
        // 如果服务器目录不存在则新建
        if(!file.exists()){
            file.mkdirs();
        }
        // 开始写
        //BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file.getAbsolutePath() + "/" + originalFilename));
        // 可以采用UUID来生成文件名,防止服务器上传文件时产生覆盖
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file.getAbsolutePath() + "/" + UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."))));
        byte[] bytes = new byte[1024 * 100];
        int readCount = 0;
        while((readCount = in.read(bytes)) != -1){
            out.write(bytes,0,readCount);
        }
        // 刷新缓冲流
        out.flush();
        // 关闭流
        in.close();
        out.close();

        return "ok";
    }

}

1.2 文件下载

前端页面:

<!--文件下载-->
<a th:href="@{/download}">文件下载</a>

文件下载核心程序,使用ResponseEntity:

@GetMapping("/download")
public ResponseEntity<byte[]> downloadFile(HttpServletResponse response, HttpServletRequest request) throws IOException {
    File file = new File(request.getServletContext().getRealPath("/upload") + "/1.jpeg");
    // 创建响应头对象
    HttpHeaders headers = new HttpHeaders();
    // 设置响应内容类型
    headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
    // 设置下载文件的名称
    headers.setContentDispositionFormData("attachment", file.getName());

    // 下载文件
    ResponseEntity<byte[]> entity = new ResponseEntity<byte[]>(Files.readAllBytes(file.toPath()), headers, HttpStatus.OK);
    return entity;
}

最后感谢动力节点提供的优质学习资源,让我们在技术道路上能够站在巨人的肩膀上继续前行。本资料仅为学习过程的副产品,希望能帮助到更多同样在努力学习的开发者。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐