HTTP 三、http在springboot中得应用
一、springboot处理http请求的过程
1、客户端发起HTTP请求,经过网络传输到服务器
HTTP请求通常由浏览器、Postman、curl或其他HTTP客户端发起,客户端的HTTP请求通过网络(通常是TCP/IP协议)传输到服务器,这个请求首先会到达Spring Boot应用的端口
2、服务器接收请求
服务器上的网络堆栈处理传入的TCP/IP包,并将其传递给服务器软件。在Spring Boot应用中,嵌入式的Tomcat、Jetty或Undertow服务器负责监听HTTP端口并接收请求。
也就是说到达springboot的程序之后,首先是由tomcat接收并处理请求的,一般这个tomcat是springboot内置的。
3、 Servlet容器(tomcat)处理请求
嵌入式的Servlet容器(如Tomcat)接收到HTTP请求后,会将请求解析成HttpServletRequest
对象。这个对象封装了请求行、请求头和请求体等信息。这个过程是在Tomcat的内部实现的,遵循Servlet规范,不涉及到Spring Boot的具体代码。
4、查找对应的Servlet然后DispatcherServlet处理请求
Tomcat Servlet容器会根据HttpServletRequest对象的信息(例如URL、HTTP方法类型等)去寻找已经注册到容器中的对应的Servlet,找到对应的DispatcherServlet
后,Tomcat将把HttpServletRequest对象传递给它。DispatcherServlet
接收到HttpServletRequest后,将会根据其中的URL信息,去寻找在Spring应用程序中注册的对应的Controller和处理的方法。
5、Controller处理请求
找到对应的Controller和方法后,DispatcherServlet
将调用这个方法,并传递HttpServletRequest中的信息。到达controller接口之后,进入controller中自己的处理逻辑,然后最终生成返回结果。
- 视图解析:如果控制器返回的是视图名称,Spring会使用
ViewResolver
来解析视图并生成HTML内容。 - 序列化数据:如果控制器返回的是对象(如JSON),Spring会使用
HttpMessageConverter
将对象序列化为JSON或XML格式。
6、返回响应给客户端
DispatcherServlet
生成HttpServletResponse
对象,包含响应的状态码、头部和体。Servlet容器(如Tomcat)将HttpServletResponse
对象转换为原始的HTTP响应,并通过网络传输回客户端。
7、客户端接收响应
客户端收到HTTP响应,解析响应行、响应头和响应体,并根据状态码和内容采取相应的动作。例如:
- 浏览器渲染HTML内容或显示JSON数据。
- API客户端解析JSON数据并进一步处理。
二、关于tomcat与servlet
Spring Boot处理HTTP请求的整个过程。在这个过程中,Spring Boot的自动配置让开发者不需要关心底层的处理细节,可以专注于编写业务代码。
而tomcat主要就是负责处理http请求的。Spring Boot默认使用内嵌的Tomcat服务器。这意味着Spring Boot应用启动时,它会自动启动一个Tomcat实例,并且该实例会根据Spring Boot的配置文件来决定监听哪个端口。
Tomcat作为Servlet容器,负责管理所有部署在它上面的应用及其Servlet对象。而servlet,在每个Spring Boot应用中(也就是每个服务中),通常会有一个DispatcherServlet
对象,它是负责处理所有HTTP请求的核心组件。每个Spring Boot应用相当于一个独立的服务,运行在Tomcat上。也就是说每一个springboot服务对应一个servlet 对象,这个servlet对象由tomcat统一管理。每个应用的DispatcherServlet
是独立的,处理与该应用相关的所有HTTP请求。
所以总结一下:
- 一个Spring Boot服务对应一个Servlet对象,即
DispatcherServlet
。 - 服务器(Tomcat)上可以运行多个Spring Boot服务,因此会有多个
DispatcherServlet
对象。 - Tomcat负责管理这些Servlet对象,包括它们的创建、初始化、请求处理、和销毁,并确保请求被正确路由到相应的Servlet对象。
这里面只简答介绍一下对应关系,详细了解可以再自行查询。
所以流程就是:
http请求 ----> tomcat ---> 对应servlet类 ----> 再到对应接口
三、springboot对应http请求映射 (Request Mapping)
前面简单说了 http 对应的方法类型常用的有 GET、POST、PUT、DELETE 等方法,这些方法在springboot中也帮我们定义好了对应的注解来映射这些方法。
- @GetMapping:用于处理 HTTP GET 请求。
- @PostMapping:用于处理 HTTP POST 请求。
- @PutMapping:用于处理 HTTP PUT 请求。
- @DeleteMapping:用于处理 HTTP DELETE 请求。
@RestController
@RequestMapping("/api")
public class MyController {
@GetMapping("/hello")
public String hello() {
return "Hello, World!";
}
}
上面的代码中,指定请求路径为 /api/hello,指定请求方法为 GET。
还有一个常用注解是@RequestMapping 注解,这个注解 value 属性来指定请求路径,使用 method 属性来指定请求方法,例如:
@RestController
@RequestMapping("/api")
public class MyController {
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello() {
return "Hello, World!";
}
}
关于这些注解的详细使用也不再多说,比较简单会用即可,详细可再自行搜索。
四、请求参数传递的方式:
springboot搓衣板接收参数的形式有四种:
1、URL传参
有两种,一种是路径拼接?传参,一种是直接拼接参数
1)params传参,params传参的格式是http://xxx?参数名=值&参数名=值
。在postman当中params当中添加参数会发现,他就是在地址栏上加的参数。
2)、地址栏传参,直接通过/在地址上拼接参数值,这种方式不需要在地址栏上写参数名,后端只需要知道他在地址的哪个位置传的参数就可以拿到值!
2、Body传参
Body表示HTTP消息中传输的数据字节,用于携带该请求或响应的有效负载正文。我们需要使用 Content-Type 表示发送数据的 MIME类型。
Content-Type表示形式为media-type;charset;boundary;如:
Content-Type: text/html; charset=UTF-8
Content-Type:multipart/form-data; boundary=something
MIME类型通用结构为type/subtype, 由类型与子类型两个字符串中间用/分隔而组成。不允许空格存在。type表示可以被分多个子类的独立类别。subtype 表示细分后的每个类型。MIME类型对大小写不敏感,但是传统写法都是小写。如果MIME类型与实际发送的数据不符服务器会无法识别。
Media类型:
- text: 纯文本数据,包括任何人类可读的内容、源代码或文本数据. 如text/plain,text/html,text/css, text/javascript
- font: 字体文件. 如font/woff,font/ttf
- model: 3D 对象或场景的模型数据. 如model/gltf-binary,model/gltf+json,model/obj
- image: 表明是某种图像。不包括视频,但包括动态图(比如动态 gif. 如
- image/gif,image/png,image/jpeg,image/webp
- audio: 音频文件, 如audio/webm, audio/wav, audio/mp4
- video: 视频文件, 如video/webm,video/wav,video/mp4
- application: 任何不明确归入其他类型之一的二进制数据;将以某种方式执行或解释的数据或需要特定应用程序或应用程序类别才能使用的二进制数据。 如application/pdf,application/xml,application/json
- multipart: 表示被分成几部分的文档类别,通常具有不同的 MIME 类型, 代表一个复合文档。 如multipart/form-data,multipart/encrypted
就是说如果我们使用 url 传参的形式,其实不用设置 Content-Type 请求头,如果要是用http body形式传参,那么我们就需要设置 Content-Type 请求,代表我们传递的是哪种形式的参数。
常见的 Content-Type
有:
application/json
:用于发送 JSON 格式的数据。application/x-www-form-urlencoded
:用于发送表单数据。multipart/form-data
:用于文件上传。
3、Headers传参
Headers允许客户端和服务器通过request和response传递附加信息,主要用来描述资源或服务器或客户端的行为.
参数作为HTTP请求的头部信息中的一部分发送。例如,可以使用Authorization
头部字段来传递身份验证令牌。
4、Cookie参数(Cookie Parameters)
参数存储在HTTP请求的Cookie中,服务器可以通过读取Cookie来获取参数值。
5、文件上传参数(File Upload Parameters)
用于上传文件时的参数,通常以表单形式发送,可以通过请求体或者请求头来传递。
五、springboot中接收参数的方式
springboot中的接口已经帮我们做好了很多准备来接收各种方式的传参。通常接收参数的方式有以下几种:
1、@RequestParam接收 URL 查询参数
通常用来接收 URL 查询参数,使用示例如下:
@GetMapping("/greet")
public String greet(@RequestParam String name) {
return "Hello, " + name;
}
需要注意的是 :
1)携带@RequestParm
的时候,required 属性默认是true
。如果不传值会报错。
2)接收参数,不可使用使用实体来封装,如下所示,直接会报400错误。
3)注意@RequestParam默认是以变量名作为前端传参名称,但是假如我们注解设置了名称,而前端传的参数名跟我们设置的不一样 就会报异常。
4)@RequestParam 也可以接收body 传参中 application/x-www-form-urlencoded 类型的参数。
2、@PathVariable
@PathVariable
用于绑定 url 中的占位符。例如:请求 url 中 /delete/{id}
,这个{id}
就是 url 占位符。url 支持占位符是 spring3.0 之后加入的。是 springmvc 支持 rest 风格 URL 的一个重要标志。
@GetMapping("/users/{id}")
public User getUserById(@PathVariable Long id) {
return userService.getUser(id);
}
需要注意的是:
1)如果@PathVariable不设置属性值,默认的话,只要参数名称和占位符当中的名称一致就可以,如果名称不一致就会报错
2)一旦设置属性值了,就一定要和占位符当中的一致,否则就会报错!
3、@RequestBody
@RequestBody一般被用来接收http请求中body中json数据。也可以接收 XML 类型数据。get、post都可以使用。一般用于post。
@PostMapping("/users")
public String createUser(@RequestBody User user) {
return "User created: " + user.getName();
}
{"userName":"111","age":"11"},传输json数据,也可以不传key。这里有一点需要注意,mvc给我们做了参数类型转换,Params 对象当中的age是Integer类型,但是json传字符串,照样可以映射进去。Boolean、BigDecimal、Integer、String、Date这些其实都可以映射进去的。当然Date相对来说比较特殊一点。传yyyy-MM-dd格式是不会报错的。但是其他格式可能就会报错了,一般使用Date类型接参数涉及到需要咱们自己格式化,关于日期相关问题感兴趣的可以看这一篇文章:
@DateTimeFormat 和 @JsonFormat 注解详解-CSDN博客
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestBody {
boolean required() default true;
}
- required默认为true,代表的就是json不能什么都不传,否则报错400,但是允许个别属性不传。
- 设置为false的话就是可以什么都不传。
4、@RequestHeader
@RequestHeader主要用来获取请求当中的请求头
@GetMapping("/headers")
public String getHeaders(@RequestHeader("User-Agent") String userAgent) {
return "User-Agent: " + userAgent;
}
我们通过header进行参数传递,同样它可以设置是否必传,默认值等,请大家自行翻阅源码,就不一一罗列了。
5、@ModelAttribute
接收表单数据
适用于接收表单提交的数据,将参数映射到对象的属性上。可以同时接收查询参数和请求体数据。当请求头是 application/x-www-form-urlencoded 时
代表表单数据,通常通过 @RequestParam
或 @ModelAttribute
来接收。
@PostMapping("/register")
public String registerUser(@ModelAttribute User user) {
return "User registered: " + user.getName();
}
6、通过 @CookieValue
接收 Cookie
用于获取客户端请求中的 Cookie 值。
@GetMapping("/cookies")
public String getCookie(@CookieValue("sessionId") String sessionId) {
return "Session ID: " + sessionId;
}
7、通过 HttpServletRequest
或 HttpServletResponse
手动处理参数
这是直接拿到request对象,通过request可以从对象中灵活的获取参数,如果需要对请求的细节进行更细粒度的控制,或者处理文件上传等特殊情况,可以使用 HttpServletRequest
。
@RestController
@RequestMapping("/request")
public class HttpServletRequestController {
@GetMapping("/getUrlValue")
public String getUrlValue(HttpServletRequest request) {
// 没有的时候不会报错,直接为null
String msg = request.getParameter("msg");
System.out.println(msg);
return msg;
}
@GetMapping("/getUrlValues")
public String getHttpServletRequestValue(HttpServletRequest request) {
Map<String, String[]> parameterMap = request.getParameterMap();
return JSONObject.toJSONString(request.getParameterMap());;
}
}
针对于request.getParameter("msg");
,其实就是跟@RequestParam差不多,可以获取到body当中的for-data
的数据以及使用url ?
拼接的参数的数据。
HttpServletRequest 获取参数的方式:
1)URL 中的查询参数:
示例:假设客户端发送的 URL 为:/example?name=John&age=25
@RequestMapping("/example")
public void handleRequest(HttpServletRequest request) {
String name = request.getParameter("name"); // 获取查询参数 name=John
String age = request.getParameter("age"); // 获取查询参数 age=25
// 处理逻辑...
}
常用方法:
request.getParameter("paramName")
:获取某个参数的值,如果参数不存在返回null
。request.getParameterValues("paramName")
:如果参数名对应多个值(如复选框等),可以使用此方法获取所有值(返回一个String[]
数组)。request.getParameterMap()
:获取所有参数,返回Map<String, String[]>
,其中键是参数名,值是对应的参数值数组。
2)获取表单数据
如果 Content-Type
是 application/x-www-form-urlencoded
或 multipart/form-data
,参数可以通过 getParameter()
方法来获取。POST 请求的参数以表单的形式发送在请求体中。
示例:客户端发送的表单数据为:name=John&age=25
@RequestMapping("/submitForm")
public void handleForm(HttpServletRequest request) {
String name = request.getParameter("name"); // 获取表单参数 name=John
String age = request.getParameter("age"); // 获取表单参数 age=25
// 处理逻辑...
}
3)获取请求体中的原始数据(适用于 JSON、XML、纯文本等)
对于 POST、PUT 请求,如果请求体中包含的是 JSON、XML 或其他格式的数据,你可以通过 HttpServletRequest
的 getInputStream()
或 getReader()
方法来获取请求体的原始数据。此时,不能使用 getParameter()
来获取这些参数。
示例:客户端发送的 JSON 数据为:{"name": "John", "age": 25}
@RequestMapping("/submitJson")
public void handleJson(HttpServletRequest request) throws IOException {
// 获取请求体中的 JSON 数据
BufferedReader reader = request.getReader();
StringBuilder json = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
json.append(line);
}
// 处理 JSON 数据,例如通过 Jackson 解析
String jsonString = json.toString();
// 解析 JSON 或其他处理逻辑...
}
4) 获取路径参数
如果参数嵌入在 URL 路径中,不能通过 getParameter()
获取,而是需要解析请求路径。
示例:假设 URL 为 /example/123
@RequestMapping("/example/{id}")
public void handlePathVariable(@PathVariable("id") String id) {
// 获取路径中的参数 id = 123
System.out.println("Path parameter id: " + id);
}
如果直接通过 HttpServletRequest
获取路径参数,可以手动解析路径:
@RequestMapping("/example/*")
public void handleRequest(HttpServletRequest request) {
String requestURI = request.getRequestURI(); // 获取请求的完整 URI
// 手动解析路径参数
String id = requestURI.substring(requestURI.lastIndexOf("/") + 1);
System.out.println("Path parameter id: " + id);
}
5) 获取 Header
中的参数
通过 HttpServletRequest
可以访问 HTTP 请求头中的参数。
示例:假设客户端发送的请求头为:Authorization: Bearer abc123
@RequestMapping("/example")
public void handleRequest(HttpServletRequest request) {
String authHeader = request.getHeader("Authorization"); // 获取请求头中的 Authorization 参数
System.out.println("Authorization: " + authHeader);
}
6) 获取文件上传内容
如果上传的是文件,可以通过 MultipartFile
类来处理文件上传,通常会与 multipart/form-data
一起使用。Spring 本身提供了更高级的处理方式,通过 @RequestParam
获取上传的文件。
@RequestMapping("/upload")
public void handleFileUpload(@RequestParam("file") MultipartFile file) {
String fileName = file.getOriginalFilename();
// 保存文件或者处理文件逻辑
}
但如果你想通过 HttpServletRequest
获取文件,可以通过 getParts()
方法:
@RequestMapping("/upload")
public void handleFileUpload(HttpServletRequest request) throws ServletException, IOException {
Part filePart = request.getPart("file"); // 获取文件部分
String fileName = filePart.getSubmittedFileName(); // 获取文件名
InputStream fileContent = filePart.getInputStream(); // 获取文件内容
// 保存文件或者处理文件逻辑
}
六、http上传文件请求解析
POST /upload HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="myfile.txt"
Content-Type: text/plain
(file content goes here)
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="description"
This is my file
------WebKitFormBoundary7MA4YWxkTrZu0gW--
上面是一个使用http请求,请求文件上传的完整请求,简单介绍一下各部分内容:
POST /upload HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
1. HTTP 请求行
POST /upload HTTP/1.1
:POST
:HTTP 请求方法,表示客户端正在向服务器发送数据(如文件上传)。/upload
:请求的目标 URL 路径,表示上传操作的资源路径。HTTP/1.1
:HTTP 协议版本,表示客户端和服务器使用的是 HTTP/1.1 版本。
2. 请求头部分
请求头提供了有关请求的附加信息。它们告诉服务器如何解析请求中的数据。
-
Host: example.com
:指定服务器的域名(或 IP 地址),在这个例子中是example.com
。对于 HTTP/1.1,Host
头是必需的。 -
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
:Content-Type: multipart/form-data
:这表示请求体的内容是以multipart/form-data
的形式发送的。这种格式主要用于文件上传,因为它能够在一个请求中包含多部分数据,如文件和文本字段。boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
:boundary
是分隔符,用于区分请求体中的不同部分。每一部分的数据以这个boundary
开头,用于告诉服务器何时开始和结束一个数据块。这个边界字符串是客户端随机生成的,确保它在整个请求中是唯一的。
3. 请求体部分
请求体中的内容分成了多个部分,每个部分通过 boundary
进行分隔。
第一部分:文件内容
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="myfile.txt"
Content-Type: text/plain
(file content goes here)
-
------WebKitFormBoundary7MA4YWxkTrZu0gW
:这是我们前面定义的boundary
,用于区分不同的部分。它用来表示每个部分的开始。 -
Content-Disposition: form-data; name="file"; filename="myfile.txt"
:Content-Disposition
:头字段,表示这部分的内容是以表单字段的形式发送的。form-data
:表示这是表单数据。name="file"
:这个字段的名字是file
,服务器端可以根据这个名字提取上传的文件。filename="myfile.txt"
:表示上传的文件名是myfile.txt
。这是文件的原始名称,服务器可以选择使用它来命名上传的文件。
-
Content-Type: text/plain
:表示上传文件的 MIME 类型是text/plain
(纯文本)。这告诉服务器如何解释文件内容。 -
(file content goes here)
:这里表示文件的实际内容。在真实的请求中,文件的二进制数据会放在这里。
第二部分:普通文本字段(描述)
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="description"
This is my file
-
------WebKitFormBoundary7MA4YWxkTrZu0gW
:和上面一样,表示每个部分的开头。每一个部分都是以boundary
分隔的。 -
Content-Disposition: form-data; name="description"
:Content-Disposition
:表示这是表单数据。name="description"
:这个字段的名字是description
,服务器端会根据这个名字读取该文本字段的值。
-
This is my file
:这是表单字段description
的值,表示文件的描述。在实际中,这是一个普通的文本字段。
请求体的结束:
------WebKitFormBoundary7MA4YWxkTrZu0gW--
------WebKitFormBoundary7MA4YWxkTrZu0gW--
:最后的boundary
,带有两个连字符--
,表示请求体的结束。
总结:
- Content-Type: multipart/form-data:告诉服务器请求体中包含多个部分,适合用于文件上传。
- boundary:边界字符串,帮助服务器识别请求体的每个部分。
- Content-Disposition:
- 表示每个部分的数据是表单字段。
name
属性表示字段名称,filename
表示文件名称。
- Content-Type:文件部分会带有 MIME 类型,告知服务器文件的类型(如
text/plain
)。 - boundary --:请求体以
boundary
开始和结束,每部分的数据都由boundary
分隔。
这种结构可以在一个请求中包含多个字段和文件,服务器可以基于字段名和内容类型正确地解析文件上传和其他表单数据。
七、springboot下载文件
上面都是在讲解上传文件以及sprongboot如何接收文件,下面主要说一下springboot如何下载文件。有两种方式 利用 HttpServletResponse 或 ResponseEntity 。下载的方式比较简单,也比较通用。如下:
1. HttpServletResponse
HttpServletResponse
是 Servlet API 的一部分,用于直接操作 HTTP 响应。它提供了较底层的操作方式,可以控制响应的各个部分,包括:
- 响应头
- 状态码
- 响应体的输出流
特点:
- 灵活性强:允许开发者直接操作响应流、设置任意响应头、输出任意格式的数据。
- 低级别:基于 Servlet API,通常需要手动设置各种细节,如
Content-Type
、Content-Disposition
等,适用于需要对 HTTP 响应进行完全控制的场景。 - 典型用例:文件下载、流式数据传输、实时响应数据。
示例:
@GetMapping("/download")
public void downloadFile(HttpServletResponse response){
try {
InputStream inputStream = "获取你得文件流";
response.addHeader("Content-disposition", "attachment; filename=" + URLEncoder.encode("文件名称", "UTF-8"));
response.addHeader("Content-type", "application/octet-stream");
IoUtil.copy(inputStream,response.getOutputStream(),IoUtil.DEFAULT_BUFFER_SIZE);
inputStream.close();
} catch (Exception e) {
log.error("下载文件失败 fileId:{}",fileId,e);
throw new BizException("下载文件失败");
}
}
2. ResponseEntity
ResponseEntity
是 Spring 提供的高级封装,允许你同时设置响应头、响应体和响应状态码。它简化了处理 HTTP 响应的工作,封装了 HttpServletResponse
的底层操作。
特点:
- 简洁和易用:通过链式方法直接设置响应的状态码、头部和 body,可以快速返回 JSON、XML 或文件等各种格式的数据。
- 高级封装:
ResponseEntity
封装了常见的 HTTP 响应操作,避免了手动处理HttpServletResponse
的低级别操作。 - 典型用例:REST API 的响应、文件下载、返回自定义的 HTTP 状态码和头部。
@GetMapping("/download")
public ResponseEntity<byte[]> downloadFile() throws IOException {
byte[] fileContent = "This is a sample file".getBytes();
// 设置响应头和 body
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"file.txt\"")
.body(fileContent);
}
HttpServletResponse
和 ResponseEntity
的关系
-
基础 vs 封装:
HttpServletResponse
是基于 Servlet API 的底层接口,而ResponseEntity
是 Spring 提供的高级封装。ResponseEntity
内部会使用HttpServletResponse
来实现实际的 HTTP 响应操作,但它通过简化的 API 隐藏了很多底层细节,便于开发者处理常见的 HTTP 响应场景。 -
灵活 vs 简洁:
HttpServletResponse
提供了完全的灵活性,适合处理需要直接操作 HTTP 响应流的情况,比如实时输出、流式处理等。ResponseEntity
则适合绝大多数 RESTful API 场景,提供了更简洁的方式来控制响应内容、头部和状态码。
何时使用哪个?
- 使用
HttpServletResponse
:- 当你需要手动处理响应流(例如文件下载或流式输出)。
- 当你需要对响应的细节进行完全控制。
- 使用
ResponseEntity
:- 当你需要处理标准的 API 响应,尤其是返回 JSON、XML、文件或常规的 HTTP 响应内容时。
- 当你想简化设置 HTTP 状态码、头部和内容的操作。
总结:
HttpServletResponse
是较低级别的工具,提供对 HTTP 响应的完全控制。ResponseEntity
是 Spring 提供的高级封装,更适合 REST API 的场景。- 根据你的需求,可以选择使用其中之一。如果你只是处理常规的 HTTP 响应,
ResponseEntity
是推荐的选择;如果你需要精细控制响应流,HttpServletResponse
是更合适的工具。