文章目录
七、HttpMessageConverter详解
HttpMessageConverter,报文信息转换器。
作用:
1、将请求报文转化为java对象
2、将java对象转化为响应报文
HttpMessageConverter提供了两个注解和两个类供我们使用:@RequestBody、@ResponseBody;RequestEntity、ResponseEntity。
下面就让我们详细来学学吧。
首先,新建一个Moodle,然后修改pom.xml内容如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>rest_mvc</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<packaging>war</packaging>
<dependencies>
<!--SpringMVC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
<!--ServletAPI-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!--Spring5和Thymeleaf整合包-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
</dependencies>
</project>
然后写我们的springMVC.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--扫描组件-->
<context:component-scan base-package="com.example"/>
<!--配置Thymeleaf视图解析器(针对h5文件,抄就完了)-->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!--视图前缀-->
<property name="prefix" value="/WEB-INF/templates/"/>
<!--视图后缀-->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
<!--
处理静态资源,如html、js、css、jpg
若只设置该标签,则只访问静态资源,其他请求无法访问
此时必须设置<mvc:annotation-driven/>解决问题
-->
<mvc:default-servlet-handler/>
<!--开启mvc注解驱动-->
<mvc:annotation-driven>
<mvc:message-converters>
<!--处理响应中文乱码问题-->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="defaultCharset" value="UTF-8"/>
<property name="supportedMediaTypes">
<list>
<value>text/html</value>
<value>application/json</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
</beans>
7.1 @RequestBody注解
@RequestBody可以获取请求体,需要在控制器方法设置一个形参,使用@RequestBody进行标识,当前请求的请求体就会为当前注解所标识的形参赋值。
直接给代码演示吧:
先写我们的首页index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>首页</h1>
<form th:action="@{/testRequestBody}" method="post">
<input type="text" name="username"><br>
<input type="password" name="password"><br>
<input type="submit" value="提交"><br>
</form>
</body>
</html>
然后写一个成功后跳转的页面success.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>成功</title>
</head>
<body>
<h1>操作成功!!!</h1>
</body>
</html>
最后写我们的控制器方法:
package com.example.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class HttpController {
@GetMapping("/")
public String toIndex(){
return "index";
}
@PostMapping("/testRequestBody")
public ModelAndView testRequestBody(ModelAndView modelAndView, @RequestBody String requestBody){
System.out.println("requestBody:"+requestBody);
modelAndView.setViewName("success");
return modelAndView;
}
}
启动服务器:
点击提交成功跳转到success页面,然后后台输出:
7.2 RequestEntity类
RequestEntity类是封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过getHeaders()方法获取请求头信息,用getBody()获取请求体信息。
废话不多说,直接贴代码:
先修改index页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>首页</h1>
<h2>测试requestBody</h2>
<form th:action="@{/testRequestBody}" method="post">
<input type="text" name="username"><br>
<input type="password" name="password"><br>
<input type="submit" value="提交"><br>
</form>
<h2>测试requestEntity</h2>
<form th:action="@{/testRequestEntity}" method="post">
<input type="text" name="username2"><br>
<input type="password" name="password2"><br>
<input type="submit" value="提交"><br>
</form>
</body>
</html>
然后添加控制器方法:
@PostMapping("/testRequestEntity")
public ModelAndView testRequestEntity(ModelAndView modelAndView, RequestEntity<String> requestEntity){
System.out.println("request-head:"+requestEntity.getHeaders());
System.out.println("request-body:"+requestEntity.getBody());
modelAndView.setViewName("success");
return modelAndView;
}
输入:
输出结果:(请求头很长很长)
请求头输出如下:
request-head:[host:“localhost:8080”, connection:“keep-alive”, content-length:“34”, cache-control:“max-age=0”, sec-ch-ua:”“Google Chrome”;v=“93”, ” Not;A Brand”;v=“99”, “Chromium”;v=“93″”, sec-ch-ua-mobile:”?0″, upgrade-insecure-requests:“1”, origin:“http://localhost:8080”, user-agent:“Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4542.2 Safari/537.36”, accept:“text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,
/
;q=0.8,application/signed-exchange;v=b3;q=0.9”, sec-fetch-site:“same-origin”, sec-fetch-mode:“navigate”, sec-fetch-user:”?1″, sec-fetch-dest:“document”, referer:“http://localhost:8080/httpMsg/”, accept-encoding:“gzip, deflate, br”, accept-language:“zh-CN,zh;q=0.9”, Content-Type:“application/x-www-form-urlencoded;charset=UTF-8”]
7.3 @ResponseBody注解
@ResponseBody注解用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应浏览器。
7.3.1 @ResponseBody注解返回字符串
为了让人容易理解,这里给一个返回字符串形式的例子。
先写Controller方法
@RequestMapping("/testResponseBody")
@ResponseBody
public String testResponseBody(){
return "success";
}
然后在index页面,写一个跳转的超链接:
<h2>测试ResponseBody传字符串</h2>
<a th:href="@{/testResponseBody}">测试ResponseBody传字符串</a><br>
重启服务器。
我们知道,如果没加@ResponseBody注解,我们应该跳转到success.html页面,但是,当加了ResponseBody注解后,显示的确实:
这是因为,我们设置了ResponseBody注解之后,返回值就不再被拦截后加什么前后缀了,而是作为响应报文响应给服务器。
此时就相当于在之前JavaWeb中,在servlet中写了方法:
public void testResponseBody(HttpServletResponse response) throws IOException {
response.getWriter().write("success");
}
7.3.2 @ResponseBody注解返回JSON字符串对象
我们的@ResponseBody注解的方法返回的值我们已经知道会作为响应报文了。但是,我们一般更多的是用来返回一个JSON对象,充当接口也好,充当别的也罢,返回更多的还是对象。
下面,就直接用代码演示啦。
先导入我们JSON的依赖:
<!--JSON数据绑定-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>
然后写我们的User类:
package com.example.po;
public class User {
private Integer id;
private String username;
private String password;
private Integer age;
private String sex;
public User() {
}
public User(Integer id, String username, String password, Integer age, String sex) {
this.id = id;
this.username = username;
this.password = password;
this.age = age;
this.sex = sex;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
然后写控制器方法:
@RequestMapping("/testResponseUser")
@ResponseBody
public User testResponseUser(){
return new User(1001,"Keeling","10086",18,"男");
}
最后在首页写超链接:
<h2>测试ResponseBody传JSON对象</h2>
<a th:href="@{/testResponseUser}">测试ResponseBody传JSON对象</a><br>
重启服务器:
这个其实就是一个JSON字符串,我们导入的JSON的包帮我们把我们的User对象转化为JSON字符串类型,然后作为响应报文返回给浏览器。
总结一下@ResponseBody处理JSON步骤如下:
- 导入jackson依赖
-
在SpringMVC配置文件中添加
<mvc:annotation-driven />
- 在处理器上使用@ResponseBody注解
- 直接在控制器方法中返回我们自己的类对象
7.4 使用ajax获取响应报文
首先添加jquery文件:
在首页引入jquery,然后加个超链接(写别的,例如按钮其实也可以):
<h2>测试Ajax接收ResponseBody传输的JSON对象</h2>
<a id="ReceiveAjax" th:href="@{/}">测试Ajax接收ResponseBody传输的JSON对象</a><br>
<script type="text/javascript" th:src="@{/}+'/static/js/jquery-3.6.0.js'"></script>
<script type="text/javascript">
$(function () {
$("#ReceiveAjax").click(function (event){
$.ajax({
url:"http://localhost:8080/httpMsg/testResponseUser",
data:"",
success:function (data){
console.log(data)
}
});
event.preventDefault();
})
})
</script>
重启服务器:
点击按钮就可以获取刚才的JSON对象响应报文信息。
想要获取他的id值,username值,只需要在success回调函数中用data.id、data.username获取即可。
7.5 @RestController注解
以后会很重要,很常用的一个注解——@RestController。这个注解是SpringMVC提供的一个复合注解,标识在控制器的类上,相当于为这个类添加@Controller注解,并且为这个类中所有的方法添加@ResponseBody注解。
7.6 ResponseEntity类
ResponseEntity用于控制器方法的返回值类型,该控制器方法的返回值就是响应到服务器的响应报文。
7.6.1 ResponseEntity实现文件下载
首先我们把我们要下载的图片放到这里:
直接贴代码吧。
先写一个Controller类:
FileUpAndDownController.java
package com.example.controller;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
@Controller
public class FileUpAndDownController {
@RequestMapping("/file")
public String toFile(){
return "file";
}
@RequestMapping("/testDownload")
public ResponseEntity<byte[]> testDownload(HttpSession session) throws IOException {
/*获取ServletContext对象*/
ServletContext context = session.getServletContext();
/*获取服务器中文件的真实路径*/
String path = context.getRealPath("/static/img/cat.jpg");
/*创建输入流*/
InputStream inputStream = new FileInputStream(path);
/*创建字节数组*/
byte[] buffer = new byte[inputStream.available()];
/*将流读取到字节数组中*/
inputStream.read(buffer);
/*创建HttpHeaders对象设置响应头信息*/
MultiValueMap<String, String> headers = new HttpHeaders();
/*设置下载的方式和文件名*/
headers.add("Content-Disposition", "attachment;filename=hello.jpg");
/*设置响应状态码*/
HttpStatus status = HttpStatus.OK;
/*创建ResponseEntity对象*/
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(buffer,headers,status);
/*关闭输入流*/
inputStream.close();
return responseEntity;
}
}
然后写一个file.html文件:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>文件的下载和上传</title>
</head>
<body>
<h2>测试文件下载</h2>
<a th:href="@{/testDownload}">点击下载cat.jsp</a><br>
</body>
</html>
重启服务器后
下载成功。
简单介绍一下,一个从服务器下载文件的程序,一般只需要修改下面两处即可。
7.7 SpringMVC实现文件上传
要实现上传功能,首先,我们需要添加这个jar包:
<!--文件上传-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
然后我们配置我们的SpringMVC配置文件,添加下面这个Bean:
<!--配置文件上传解析器,将上传的文件封装为MultipartFile-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
之后在我们的file.html文件中添加如下内容
<h2>测试文件上传</h2>
<!--
enctype="multipart/form-data"表示通过二进制形式传输
只有这样,服务器才能成功接收文件
-->
<form th:action="@{/testUpload}" method="post" enctype="multipart/form-data">
图片:<input type="file" name="photo"><br>
<input type="submit" value="提交">
</form>
然后写我们的控制器方法:
/**
* SpringMVC将我们当前上传的文件封装到MultipartFile中
* 然后使用该对象的transferTo方法实现上传即可
*/
@RequestMapping("/testUpload")
public ModelAndView testUpload(MultipartFile photo, HttpSession session, ModelAndView modelAndView) throws IOException {
/*获取上传的文件名*/
String name = photo.getOriginalFilename();
/*获取上传文件的后缀名*/
String suffix = name.substring(name.lastIndexOf("."));
/*将UUID作为文件名*/
String uuid = UUID.randomUUID().toString();
//将uuid和后缀名拼接后成为最终的文件名
name = uuid + suffix;
/*获取服务器中upload目录的路径*/
ServletContext context = session.getServletContext();
String photoPath = context.getRealPath("upload");
/*判断photoPath所对应路径是否存在*/
File file = new File(photoPath);
//不存在创建目录
if (!file.exists()){
file.mkdir();
}
/*
设置上传后的文件路径(包括文件名)
File.separator表示的是文件的分隔符
*/
String filePath = photoPath + File.separator + name;
/*将该文件上传到服务器*/
photo.transferTo(new File(filePath));
modelAndView.setViewName("success");
return modelAndView;
}
重启服务器:
选择自己本地的图片进行上传,我这里传了三四张
然后我们到target目录下,可以看到: