HttpMessageConverter,报文转换器,即将请求报文转换为Java对象或者将Java对象转换为响应报文。
请求报文转换为Java对象 | Java对象转换为响应报文 |
---|---|
@RequestBody | @ResponseBody |
RequestEntity | ResponseEntity |
- @RequestBody,将请求体转换为Java对象。注意哈,如果请求报文中没有请求体,会报错:“Required request body is missing”。
- RequestEntity,将整个请求报文转换为Java对象。完整的请求报文,包含请求行、请求头和请求体。
- @ResponseBody,用来标注控制器方法,控制器方法返回给浏览器的响应报文的Content-Type为application/json。
- ResponseEntity,用来表示控制器方法返回值的类型,适用场景:文件的上传和下载。
@RequestBody和RequestEntity
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Home</title>
</head>
<body>
<button onclick="testRequestBody()">测试@RequestBody</button><br/>
<button onclick="testRequestEntity()">测试RequestEntity</button>
<script th:src="@{/static/js/axios.min.js}"></script>
<script th:inline="javascript">
//获取上下文路径
var contextPath = /*[[@{/}]]*/'';
function testRequestBody(){
axios({
method:"post",
url:contextPath+"testRequestBody",
data:{
"username":"admin",
"password":"123456"
}
})
}
function testRequestEntity(){
axios({
method:"post",
url:contextPath+"testRequestEntity",
data:{
"username":"admin",
"password":"123456"
}
})
}
</script>
</body>
</html>
package com.example.mvc.controller;
import com.example.mvc.bean.User;
import org.springframework.http.RequestEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class TestController{
@RequestMapping(value = "/testRequestBody",method = RequestMethod.POST)
public String testRequestBody(@RequestBody User user){
System.out.println(user);
System.out.println(user.getUsername()+","+user.getPassword());
return "success";
}
@RequestMapping(value = "/testRequestEntity",method = RequestMethod.POST)
public String testRequestEntity(RequestEntity requestEntity){
System.out.println("url="+requestEntity.getUrl());
System.out.println("method="+requestEntity.getMethod());
System.out.println("headers="+requestEntity.getHeaders());
System.out.println("body"+requestEntity.getBody());
return "success";
}
}
启动应用,分别点击测试@RequestBody、测试RequestEntity,idea控制台依次打印出如下信息:
14:56:08.127 [http-nio-8080-exec-5] DEBUG org.springframework.web.servlet.DispatcherServlet - POST "/demo6/testRequestBody", parameters={}
14:56:08.128 [http-nio-8080-exec-5] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped to com.example.mvc.controller.TestController#testRequestBody(User)
14:56:08.227 [http-nio-8080-exec-5] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Read "application/json;charset=UTF-8" to [User(id=null, username=admin, password=123456)]
User(id=null, username=admin, password=123456)
admin,123456
14:56:15.205 [http-nio-8080-exec-6] DEBUG org.springframework.web.servlet.DispatcherServlet - POST "/demo6/testRequestEntity", parameters={}
14:56:15.205 [http-nio-8080-exec-6] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped to com.example.mvc.controller.TestController#testRequestEntity(RequestEntity)
14:56:15.217 [http-nio-8080-exec-6] DEBUG org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor - Read "application/json;charset=UTF-8" to [{username=admin, password=123456}]
url=http://localhost:8080/demo6/testRequestEntity
method=POST
headers=[host:"localhost:8080", connection:"keep-alive", content-length:"40", sec-ch-ua:"" Not A;Brand";v="99", "Chromium";v="96", "Google Chrome";v="96"", accept:"application/json, text/plain, */*", sec-ch-ua-mobile:"?0", user-agent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36", sec-ch-ua-platform:""Windows"", origin:"http://localhost:8080", sec-fetch-site:"same-origin", sec-fetch-mode:"cors", sec-fetch-dest:"empty", referer:"http://localhost:8080/demo6/", accept-encoding:"gzip, deflate, br", accept-language:"zh-CN,zh;q=0.9,en;q=0.8", cookie:"Idea-ac70e2ce=07de2674-103f-44e4-baab-b1bd358ffa55", Content-Type:"application/json;charset=UTF-8"]
body{username=admin, password=123456}
@ResponseBody和@RestController
@ResponseBody用来标注控制器方法,该控制器方法的返回值作为响应报文返回给浏览器。
举个例子,来比较下 使用了@ResponseBody标注控制器方法 和 没有使用@ResponseBody标注控制器方法 时,返回给浏览器的响应报文有什么区别。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Home</title>
</head>
<body>
<button onclick="testRequestBody()">没有用@ResponseBody标注控制器方法</button><br/>
<button onclick="testResponseBody()">使用了@ResponseBody标注控制器方法</button><br/>
<script th:src="@{/static/js/axios.min.js}"></script>
<script th:inline="javascript">
//获取上下文路径
var contextPath = /*[[@{/}]]*/'';
function testRequestBody(){
axios({
method:"post",
url:contextPath+"testRequestBody",
data:{
"username":"admin",
"password":"123456"
}
})
}
function testResponseBody(){
axios({
method:"post",
url:contextPath+"testResponseBody",
data:{
"username":"admin",
"password":"123456"
}
})
}
</script>
</body>
</html>
package com.example.mvc.controller;
import com.example.mvc.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class TestController{
@RequestMapping(value = "/testRequestBody",method = RequestMethod.POST)
public String testRequestBody(@RequestBody User user){
System.out.println(user);
System.out.println(user.getUsername()+","+user.getPassword());
return "success";
}
@RequestMapping(value = "/testResponseBody",method = RequestMethod.POST)
@ResponseBody
public String testResponseBody(@RequestBody User user){
System.out.println(user);
System.out.println(user.getUsername()+","+user.getPassword());
return "success";
}
}
可以明显的看到,
没有用@ResponseBody标识的控制器方法,返回给浏览器的响应报文的Content-Type是text/html。
使用了@ResponseBody标识的控制器方法,返回给浏览器的响应报文的Content-Type是application/json。
RestFul接口返回的一般都是json数据,要想控制器方法返回的Java对象以json数据格式的形式响应给浏览器,必须
- 添加依赖jackson-databind
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
</dependency>
- 开启注解驱动
<mvc:annotation-driven/>
- 控制器方法添加@ResponseBody注解
- 控制器方法的返回值是一个Java对象
看个具体实例就更好理解了。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Home</title>
</head>
<body>
<a th:href="@{/testJson}">测试json</a><br/>
<button onclick="testAxios()">测试axios</button>
<script th:src="@{/static/js/axios.min.js}"></script>
<script th:inline="javascript">
//获取上下文路径
var contextPath = /*[[@{/}]]*/'';
function testAxios(){
axios({
method:"post",
url:contextPath+"/testAxios",
data:{
"username":"admin",
"password":"123456"
}
}).then(function(resp){
console.log(resp);
console.log(resp.data);
})
}
</script>
</body>
</html>
package com.example.mvc.controller;
import com.example.mvc.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class TestController {
@RequestMapping(value = "/testJson", method = RequestMethod.GET)
@ResponseBody
public User testJson() {
User user = new User(1, "admin", "123456");
return user;
}
@RequestMapping(value = "/testAxios", method = RequestMethod.POST)
@ResponseBody
public User testAxios(@RequestBody User user) {
User newUser = new User(2, user.getUsername(), user.getPassword());
return newUser;
}
}
@RestController是SpringMVC提供的一个复合注解,相当于@Controller+@ResponseBody。
@RestController标注在某个类上,相当于
- @Controlller标注了这个类;
- @ResponseBody标注了这个类中的所有方法。
ResponseEntity
下载文件
在webapp目录下新建目录static,并在static下新建子目录img用于存放静态图片,比如1.png。
<!-- index.html -->
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Home</title>
</head>
<body>
<a th:href="@{/download}">下载图片</a>
</body>
</html>
package com.example.mvc.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 org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
@Controller
public class TestController {
@RequestMapping(value = "/download",method = RequestMethod.GET)
public ResponseEntity<byte[]> download(HttpSession session) throws IOException {
ServletContext context = session.getServletContext();
//获取文件部署在服务器上的真实路径
String realPath = context.getRealPath("/static/img/1.png");
//创建输入流
InputStream is = new FileInputStream(realPath);
//创建字节数组
byte[] bytes = new byte[is.available()];
//将输入流对应文件的所有字节读入字节数组
is.read(bytes);
//创建HttpHeaders对象,来设置响应头信息
MultiValueMap<String,String> headers = new HttpHeaders();
//设置下载方式(以附件形式下载),设置文件名称
headers.add("Content-Disposition","attachment;filename=1.png");
//设置响应状态码
HttpStatus statusCode = HttpStatus.OK;
//创建ResponseEntity对象
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes,headers,statusCode);
//关闭流
is.close();
return responseEntity;
}
}
上传文件
首先,pom.xml中添加文件上传需要的依赖:common-fileupload。
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
然后,在springMVC.xml中配置文件上传解析器,该解析器将上传的文件封装为MultipartFile类型。
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
最后,就是修改html和java代码。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Home</title>
</head>
<body>
<a th:href="@{/download}">下载图片</a>
<form th:action="@{/upload}" method="post" enctype="multipart/form-data">
头像:<input type="file" name="photo" /><br/>
<input type="submit" />
</form>
</body>
</html>
package com.example.mvc.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 org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
@Controller
public class TestController {
@RequestMapping(value = "/download",method = RequestMethod.GET)
public ResponseEntity<byte[]> download(HttpSession session) throws IOException {
ServletContext context = session.getServletContext();
//获取文件部署在服务器上的真实路径
String realPath = context.getRealPath("/static/img/1.png");
//创建输入流
InputStream is = new FileInputStream(realPath);
//创建字节数组
byte[] bytes = new byte[is.available()];
//将输入流对应文件的所有字节读入字节数组
is.read(bytes);
//创建HttpHeaders对象,来设置响应头信息
MultiValueMap<String,String> headers = new HttpHeaders();
//设置下载方式(以附件形式下载),设置文件名称
headers.add("Content-Disposition","attachment;filename=1.png");
//设置响应状态码
HttpStatus statusCode = HttpStatus.OK;
//创建ResponseEntity对象
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes,headers,statusCode);
//关闭流
is.close();
return responseEntity;
}
@RequestMapping("/upload")
public String upload(MultipartFile photo,HttpSession httpSession) throws IOException {
// System.out.println(photo.getName());
// System.out.println(photo.getOriginalFilename());
String filename = photo.getOriginalFilename();
ServletContext servletContext = httpSession.getServletContext();
String photoPath = servletContext.getRealPath("photo");
File file = new File(photoPath);
//判断photoPath所对应的路径是否存在
if(!file.exists()){
//若不存在,则创建目录
file.mkdir();
}
String finalPath = photoPath + File.separator + filename;
photo.transferTo(new File(finalPath));
return "success";
}
}
解决文件上传重名问题:上传后的文件不再使用原来的名字,使用uuid和后缀拼接得到新的文件名。
@RequestMapping("/upload")
public String upload(MultipartFile photo,HttpSession httpSession) throws IOException {
// System.out.println(photo.getName());
// System.out.println(photo.getOriginalFilename());
//获取上传的文件的文件名
String filename = photo.getOriginalFilename();
//获取上传的文件的后缀名
String suffixname = filename.substring(filename.lastIndexOf("."));
//使用uuid作为文件名
String uuid = UUID.randomUUID().toString();
//将uuid和后缀名拼接作为最终的文件名
filename = uuid + suffixname;
ServletContext servletContext = httpSession.getServletContext();
String photoPath = servletContext.getRealPath("photo");
File file = new File(photoPath);
//判断photoPath所对应的路径是否存在
if(!file.exists()){
//若不存在,则创建目录
file.mkdir();
}
String finalPath = photoPath + File.separator + filename;
photo.transferTo(new File(finalPath));
return "success";
}