HttpServletResponse 和 ResponseEntity
# HttpServletResponse 和 ResponseEntity
前言
HttpServletResponse
和 ResponseEntity
在 Spring 应用中是两个不同层次的概念,虽然它们都可以用来构建 HTTP 响应,但它们的职责、应用场景和使用方式有所不同。
# HttpServletResponse 配置项
HttpServletResponse
是 Servlet 标准中的低级 API,适用于需要直接控制 HTTP 响应的场景。与 ResponseEntity
相比,这种方式更加灵活,但需要手动处理资源管理、响应头设置等操作。
# 1. 基本使用
HttpServletResponse
用于设置响应数据,如响应状态码、响应头、Cookie、输出流等。
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@RestController
@RequestMapping("/api")
public class ResponseController {
@GetMapping("/custom-response")
public void setCustomResponse(HttpServletResponse response) {
try {
// 1. 设置 HTTP 响应状态码,例如 200 OK
response.setStatus(HttpServletResponse.SC_OK);
// 2. 设置自定义响应头,例如 "Custom-Header: CustomValue"
response.setHeader("Custom-Header", "CustomValue");
// 3. 设置响应的内容类型和字符编码
response.setContentType("application/json;charset=UTF-8");
// 4. 获取输出流并写入 JSON 数据到响应体中
response.getWriter().write("{\"message\":\"自定义响应内容\"}");
// 5. 刷新输出流,确保数据完全发送到客户端
response.getWriter().flush();
// 6. 添加一个 Cookie 到响应中
Cookie cookie = new Cookie("sessionId", "12345");
cookie.setMaxAge(60 * 60); // 设置 Cookie 的有效期为 1 小时
response.addCookie(cookie);
// 7. 如果需要,执行重定向操作
// response.sendRedirect("/login");
} catch (IOException e) {
e.printStackTrace();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
关键点说明:
response.setStatus(HttpServletResponse.SC_OK)
:设置 HTTP 响应状态码(如 200 OK)。response.setHeader("Custom-Header", "CustomValue")
:设置自定义响应头。response.setContentType("application/json;charset=UTF-8")
:设置响应的内容类型和字符编码。response.getWriter().write()
:向响应体中写入数据。response.getWriter().flush()
:刷新输出流,确保数据完全发送到客户端。response.addCookie(cookie)
:向响应中添加一个 Cookie。response.sendRedirect("/login")
:执行重定向操作。
# 1. 设置响应内容类型
Content-Type
决定了返回的数据格式,浏览器或前端客户端会根据此设置决定如何处理返回的数据。
response.setContentType("application/pdf");
常见的 Content-Type
示例:
- 文本文件:
text/plain
- HTML 文件:
text/html
- JSON 数据:
application/json
- PDF 文件:
application/pdf
- 图片文件:
image/jpeg
、image/png
- 二进制文件:
application/octet-stream
(适用于任意文件)
关键点:
Content-Type
决定了返回数据的 MIME 类型,必须与实际数据格式匹配。- 不同类型的数据需设置不同的
Content-Type
,否则可能导致前端解析错误。
# 2. 设置内容处置方式 (Content-Disposition
)
Content-Disposition
决定了返回的数据是直接展示还是作为附件下载。
response.setHeader("Content-Disposition", "attachment; filename=\"sample.pdf\"");
attachment
:以附件的形式下载文件。浏览器会弹出下载窗口,提示用户保存文件。inline
:以嵌入的形式展示文件,适用于在浏览器中直接预览 PDF、图片等文件。
关键点:
filename="sample.pdf"
:指定文件的下载名称,可以动态生成。- 当希望浏览器直接展示文件时,将
attachment
改为inline
。
# 3. 设置内容长度 (Content-Length
)
Content-Length
告诉客户端返回的数据长度(字节数)。设置该值可以帮助客户端优化下载体验。
response.setContentLength((int) file.length());
关键点:
Content-Length
是可选的,但在处理大文件时设置它有助于客户端显示进度条或控制下载进程。- 需要确保长度值与实际数据一致,否则可能会导致文件截断或显示不全。
# 4. 设置缓存控制 (Cache-Control
)
Cache-Control
控制客户端和中间代理(如 CDN、浏览器缓存)对数据的缓存策略。
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
2
3
no-cache
:强制每次都从服务器获取最新数据。no-store
:禁止缓存数据,确保敏感信息不被缓存。must-revalidate
:客户端必须验证缓存是否过期。
关键点:
- 在处理敏感数据时,可以通过这些头部确保数据不会被缓存。
- 适用于动态生成的文件或一次性数据。
# 5. 设置跨域资源共享 (CORS) 头部
在需要支持跨域请求时,可以设置 CORS 相关的响应头。
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.setHeader("Access-Control-Allow-Headers", "Content-Type");
2
3
Access-Control-Allow-Origin
:指定允许的域,*
表示允许所有域。Access-Control-Allow-Methods
:指定允许的 HTTP 方法。Access-Control-Allow-Headers
:指定允许的请求头。
关键点:
- CORS 设置适用于跨域文件下载或前端跨域请求的数据流。
- 根据实际需求配置具体的跨域策略。
# 6. 完整的配置示例
下面展示一个文件下载的完整配置,包括设置内容类型、文件处置方式、缓存控制等。
@RestController
@RequestMapping("/api")
public class FileController {
@GetMapping("/download-file")
public void downloadFile(HttpServletResponse response) {
String filePath = "C:/files/sample.pdf";
File file = new File(filePath);
if (file.exists()) {
try (InputStream inputStream = new FileInputStream(file)) {
// 设置响应内容类型
response.setContentType("application/pdf");
// 设置内容处置方式(附件下载)
response.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"");
// 设置内容长度
response.setContentLength((int) file.length());
// 设置缓存控制,禁止缓存
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
// 设置跨域支持(如有需要)
response.setHeader("Access-Control-Allow-Origin", "*");
// 将文件数据写入响应流
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
response.getOutputStream().write(buffer, 0, bytesRead);
}
response.getOutputStream().flush(); // 确保数据发送完毕
} catch (IOException e) {
e.printStackTrace();
}
} else {
// 处理文件不存在的情况
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
总结
在向响应体中写入流数据时,常用的配置项包括:
Content-Type
:设置数据的 MIME 类型,决定前端如何解析。Content-Disposition
:控制数据是直接展示还是作为附件下载。Content-Length
:告知数据的大小,有助于优化客户端体验。Cache-Control
:控制缓存策略,适用于动态生成的数据或敏感数据。CORS
设置:支持跨域请求时,需配置跨域资源共享策略。
# ResponseEntity 配置项
ResponseEntity
是 Spring 提供的一个用来构建 HTTP 响应的高级工具类,它不仅可以设置响应体的数据,还可以设置响应头、状态码等内容。相比 HttpServletResponse
,ResponseEntity
更加简洁、优雅,且更符合 Spring 框架的使用习惯。
# 1. 基本使用
ResponseEntity
提供了一种更加面向对象的方式来设置 HTTP 响应。常见的配置项包括:
- 响应体:可以是对象、字符串、文件、二进制数据等。
- 响应状态码:可以是 HTTP 标准状态码(如 200、404、500 等)。
- 响应头:可以设置自定义的响应头信息。
# 2. 设置响应体
ResponseEntity
可以通过构造方法或者静态方法来设置响应体。
// 设置响应体
ResponseEntity<String> response = new ResponseEntity<>("响应内容", HttpStatus.OK);
2
- 构造函数:
new ResponseEntity<>(body, status)
,其中body
是响应体,status
是响应状态码。
# 3. 设置响应状态码
响应状态码可以通过构造方法或者静态方法进行设置。
// 使用构造方法设置响应状态码
ResponseEntity<String> response = new ResponseEntity<>("内容", HttpStatus.CREATED);
// 使用静态方法设置响应状态码
ResponseEntity<String> response = ResponseEntity.status(HttpStatus.NOT_FOUND).body("资源未找到");
2
3
4
5
HttpStatus
:Spring 提供的枚举类,包含所有常见的 HTTP 状态码,如HttpStatus.OK
、HttpStatus.BAD_REQUEST
、HttpStatus.INTERNAL_SERVER_ERROR
等。
# 4. 设置响应头
可以通过 headers
参数或者使用 HttpHeaders
类来设置响应头。
HttpHeaders headers = new HttpHeaders();
headers.add("Custom-Header", "HeaderValue");
// 使用构造方法设置响应头
ResponseEntity<String> response = new ResponseEntity<>("内容", headers, HttpStatus.OK);
// 使用静态方法设置响应头
ResponseEntity<String> response = ResponseEntity.ok()
.headers(headers)
.body("内容");
2
3
4
5
6
7
8
9
10
HttpHeaders
:Spring 提供的响应头类,可以使用add
、set
等方法添加或设置响应头信息。
# 5. 常用静态方法
Spring 提供了一些常用的静态方法来创建 ResponseEntity
,这些方法简化了常见的 HTTP 响应场景。
// 返回 200 OK 响应
ResponseEntity.ok("成功响应");
// 返回 201 CREATED 响应
ResponseEntity.status(HttpStatus.CREATED).body("资源已创建");
// 返回 404 NOT FOUND 响应
ResponseEntity.status(HttpStatus.NOT_FOUND).body("资源未找到");
// 返回 500 INTERNAL SERVER ERROR 响应
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("服务器错误");
2
3
4
5
6
7
8
9
10
11
# 6. 使用 ResponseEntity
返回文件或二进制数据
除了返回字符串或对象,ResponseEntity
还可以返回文件、图片等二进制数据。
@GetMapping("/download")
public ResponseEntity<byte[]> downloadFile() {
byte[] fileData = ... // 获取文件数据
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDisposition(ContentDisposition.builder("attachment").filename("sample.pdf").build());
return new ResponseEntity<>(fileData, headers, HttpStatus.OK);
}
2
3
4
5
6
7
8
9
10
MediaType
:设置响应内容的类型,如MediaType.APPLICATION_PDF
、MediaType.IMAGE_JPEG
、MediaType.APPLICATION_OCTET_STREAM
等。ContentDisposition
:用于设置文件下载时的响应头,如设置为attachment
以提示浏览器下载文件,或设置为inline
直接在浏览器中显示。
# 7. ResponseEntity
的综合示例
@RestController
@RequestMapping("/api")
public class FileController {
/**
* 返回文件流作为响应体
*/
@GetMapping("/download")
public ResponseEntity<byte[]> downloadFile() {
// 假设这是文件的二进制数据
byte[] fileData = ...;
// 设置响应头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); // 设置内容类型
headers.setContentDisposition(ContentDisposition.builder("attachment").filename("sample.pdf").build()); // 设置下载文件名
// 返回包含文件数据的 ResponseEntity
return new ResponseEntity<>(fileData, headers, HttpStatus.OK);
}
/**
* 返回 JSON 数据
*/
@GetMapping("/json")
public ResponseEntity<Map<String, String>> getJson() {
Map<String, String> data = new HashMap<>();
data.put("message", "成功返回 JSON 数据");
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_JSON) // 设置内容类型为 JSON
.body(data);
}
/**
* 返回自定义状态码和头部信息
*/
@GetMapping("/custom-response")
public ResponseEntity<String> customResponse() {
HttpHeaders headers = new HttpHeaders();
headers.add("Custom-Header", "HeaderValue");
return ResponseEntity.status(HttpStatus.CREATED) // 返回 201 CREATED 状态
.headers(headers) // 设置自定义头部信息
.body("资源已创建"); // 设置响应体内容
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
总结
ResponseEntity
是 Spring 提供的一个灵活、强大的响应构建工具,允许设置响应体、响应头、状态码等信息。- 常用的配置项包括:
body
:响应体内容,支持对象、字符串、二进制数据等。HttpStatus
:HTTP 状态码,使用 Spring 的枚举类设置。HttpHeaders
:自定义响应头,可以灵活设置各种头部信息,如Content-Type
、Content-Disposition
等。
# 前端处理流数据的方式
提示
- 数据类型过滤:浏览器的开发者工具会根据不同的请求类型进行分类。在“
Fetch/XHR
”选项卡中,只显示通过fetch
或XHR
发起的请求,这些请求通常是针对JSON
、XML
、文本
等结构化数据。如果后端返回的是二进制流,如图片、音频、视频等,这些请求可能不会出现在“Fetch/XHR
”中。 Content-Type
与 MIME 类型的匹配:如果请求的Content-Type
是image/jpeg
、application/pdf
等非文本类型,浏览器可能将这些请求归类到“图片
”、“媒体”等其他类型的分类下,而不是“Fetch/XHR
”。- 捕获的请求类型不同:如果你在前端通过
<img>
标签、<iframe>
或video
标签等直接加载二进制数据,这些请求不会归类到“Fetch/XHR
”中,而是归到“图片
”、“媒体”等。
# 1. 使用 Blob
对象处理流数据
场景:适用于文件下载、图片展示、PDF 文件预览等场景。
Blob
(Binary Large Object)是浏览器提供的一个对象,用于表示二进制数据。前端可以将流数据转换为 Blob
对象,并生成 URL 用于展示或下载。
代码示例:
import axios from 'axios';
// 文件下载示例
function downloadFile() {
axios.get('/api/download', {
responseType: 'blob' // 指定响应类型为 blob,接收二进制数据
}).then(response => {
// 创建一个 Blob 对象
const blob = new Blob([response.data], { type: 'application/pdf' });
// 创建一个临时的下载链接
const downloadUrl = URL.createObjectURL(blob);
// 创建一个 <a> 标签用于触发下载
const link = document.createElement('a');
link.href = downloadUrl;
link.download = 'sample.pdf'; // 指定下载文件名
document.body.appendChild(link);
link.click(); // 模拟点击触发下载
// 释放 URL 对象
URL.revokeObjectURL(downloadUrl);
}).catch(error => {
console.error('文件下载失败:', error);
});
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
关键点:
responseType: 'blob'
:告诉 Axios 需要接收二进制数据。new Blob([response.data], { type: 'application/pdf' })
:将响应数据转换为Blob
对象,并指定 MIME 类型。URL.createObjectURL(blob)
:将Blob
对象生成一个临时的 URL,用于在浏览器中访问数据。link.download
:设置下载文件的默认名称。
# 2. 直接在 <img>
标签中展示图片流
场景:适用于展示后端返回的图片,如用户头像、展示的图片等。
前端可以通过设置 <img>
标签的 src
属性为生成的 Blob
URL 直接展示图片。
代码示例:
import axios from 'axios';
// 图片展示示例
function loadImage() {
axios.get('/api/image', {
responseType: 'blob' // 指定响应类型为 blob
}).then(response => {
// 将 Blob 对象转换为 URL
const imageUrl = URL.createObjectURL(response.data);
// 将 URL 设置为 <img> 标签的 src 属性
document.getElementById('imagePreview').src = imageUrl;
}).catch(error => {
console.error('图片加载失败:', error);
});
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
关键点:
- 同样使用
responseType: 'blob'
来接收图片的二进制数据。 - 通过
URL.createObjectURL
将Blob
对象转换为 URL,赋值给<img>
标签的src
属性。
# 3. 通用文件预览
提示
前端通过这种方式可以预览多种文件类型,只要后端返回相应的文件流并正确设置 Content-Type
,前端都可以使用 URL.createObjectURL
将文件流转换成 URL,再通过 window.open(url)
在新窗口中打开并预览。
后端返回文件流:无论是 PDF、Excel、Word、Markdown 等文件类型,后端都以文件流的形式返回数据,并设置正确的
Content-Type
(如application/pdf
、application/vnd.ms-excel
、application/msword
等)。前端接收文件流并生成 URL:
- 前端通过设置
responseType: 'blob'
来接收文件流。 - 使用
URL.createObjectURL
将文件流转换为可访问的 URL。
- 前端通过设置
打开新窗口预览文件:
- 使用
window.open(url)
打开生成的 URL,浏览器会根据返回的Content-Type
识别文件类型并自动选择合适的方式预览(如内置的 PDF、Word 预览器等)。
- 使用
import axios from 'axios';
// 通用文件预览示例
function previewFile(fileType) {
axios.get(`/api/file?type=${fileType}`, {
responseType: 'blob' // 指定响应类型为 blob
}).then(response => {
// 将 Blob 对象转换为 URL
const fileUrl = URL.createObjectURL(response.data);
// 在新窗口中打开文件预览
window.open(fileUrl);
}).catch(error => {
console.error(`${fileType} 文件加载失败:`, error);
});
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
支持的文件类型示例:
- PDF:
application/pdf
- Excel:
application/vnd.ms-excel
或application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
- Word:
application/msword
或application/vnd.openxmlformats-officedocument.wordprocessingml.document
- Markdown: 通常返回
text/markdown
或text/plain
注意事项
- 浏览器支持:不同文件类型的预览取决于浏览器是否原生支持。例如,PDF 和部分 Office 文件(如 Word、Excel)在现代浏览器中通常可以直接预览,但 Markdown 文件通常需要特定的插件或处理方式。
- 文件大小与性能:对于较大的文件,预览可能会占用大量内存,影响性能。
- 安全性:在生产环境中,要注意文件流的权限控制和 CORS 配置,避免未经授权的访问。
# 4. 使用 FileReader 处理流数据
场景:适用于需要进一步解析流数据,如读取文件内容、展示文件文本等。
FileReader
是浏览器提供的一个 API,允许读取 Blob
或 File
对象的内容,支持文本、URL、ArrayBuffer 等多种格式。
代码示例:
import axios from 'axios';
// 使用 FileReader 读取文件内容示例
function readTextFile() {
axios.get('/api/text-file', {
responseType: 'blob' // 接收二进制数据
}).then(response => {
const reader = new FileReader();
// 读取文件为文本内容
reader.readAsText(response.data, 'UTF-8');
// 读取完成后的回调
reader.onload = function() {
console.log('文件内容:', reader.result); // 输出文件文本内容
};
}).catch(error => {
console.error('文件读取失败:', error);
});
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
关键点:
FileReader.readAsText
:将Blob
对象读取为文本。FileReader
支持读取多种格式,如文本(readAsText
)、URL(readAsDataURL
)、ArrayBuffer(readAsArrayBuffer
)等。
# 前端处理流数据时的注意事项
正确设置
responseType
:- 在 Axios 中,必须将
responseType
设置为'blob'
,否则无法正确接收二进制数据。
- 在 Axios 中,必须将
MIME 类型的处理:
- 在处理不同类型的文件时,必须正确设置 MIME 类型(如
application/pdf
、image/jpeg
、application/octet-stream
等)。这决定了浏览器如何解释数据。(MIME 类型是由后端在返回响应时设置的)
- 在处理不同类型的文件时,必须正确设置 MIME 类型(如
流数据的生命周期管理:
- 通过
URL.createObjectURL
生成的 URL 占用内存,需要在使用后通过URL.revokeObjectURL
释放资源,避免内存泄漏。
- 通过
跨域问题:
- 如果后端流数据接口存在跨域问题,需要确保后端正确设置 CORS 头部,允许前端跨域访问。