作为Java开发人员,日常Web开发中高频需要从请求头 (获取请求元信息,如Token、Content-Type)、请求体 (获取客户端提交的业务数据)、响应体 (获取接口返回的结果,如第三方接口调用、内部服务间调用)中解析数据,且因请求方式(GET/POST/PUT等)、数据格式(JSON/表单/文件)、开发场景(原生Servlet/SpringBoot)的差异,获取方式不同。本文将按「请求头→请求体→响应体」三大模块 ,结合原生Servlet 和主流SpringBoot两种开发场景,搭配可直接运行的示例,系统总结所有常用获取方式,覆盖99%的业务场景,方便你直接参考使用。
一、请求头(HttpHeader)数据获取
请求头用于传递请求的元数据信息 (非业务数据),如Token、请求来源、数据格式(Content-Type)、字符编码等,所有HTTP请求(GET/POST/PUT/DELETE)都有请求头,获取方式与请求方式无关,仅分「原生Servlet」和「SpringBoot」两种场景。
核心说明
- 请求头的核心API是
HttpServletRequest(Servlet规范),SpringBoot底层也是基于此封装,可直接注入使用; - 常用操作:获取单个请求头 、获取所有请求头 、判断请求头是否存在;
- 高频常用请求头:
Content-Type(请求体格式)、Authorization(令牌/Token)、User-Agent(客户端信息)、Accept(客户端支持的响应格式)。
1.1 原生Servlet 场景(基础,理解底层)
需在Servlet中通过HttpServletRequest对象操作,直接从方法参数获取该对象即可。
java
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
// 注解配置Servlet路径
@WebServlet("/servlet/header")
public class HeaderServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response); // GET/POST统一处理,请求头获取与请求方式无关
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
// 1. 获取单个请求头(常用:Authorization/Content-Type/User-Agent)
String token = request.getHeader("Authorization"); // 示例:Bearer eyJhbGciOiJIUzI1NiJ9
String contentType = request.getHeader("Content-Type"); // 示例:application/json
String userAgent = request.getHeader("User-Agent"); // 客户端浏览器/Postman信息
// 2. 判断请求头是否存在
boolean hasToken = request.containsHeader("Authorization");
// 3. 获取所有请求头(返回枚举,遍历所有头名称和值)
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
String headerValue = request.getHeader(headerName);
System.out.println("请求头:" + headerName + " = " + headerValue);
}
// 结果输出
response.getWriter().write("单个Token:" + token + "<br/>");
response.getWriter().write("Content-Type:" + contentType + "<br/>");
response.getWriter().write("是否有Token:" + hasToken + "<br/>");
}
}
1.2 SpringBoot 场景(主流开发,极简封装)
SpringBoot中无需手动处理Servlet,可通过直接注入HttpServletRequest 或注解@RequestHeader 两种方式获取,后者更简洁,推荐日常使用。
方式1:@RequestHeader 注解(推荐,直接绑定到方法参数)
支持单个头 、默认值 、是否必传 、绑定所有头到Map/HttpHeaders,灵活适配各种场景。
java
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
@RestController
@RequestMapping("/spring/header")
public class SpringHeaderController {
// 1. 获取单个请求头(必传,若不存在会抛400异常)
@PostMapping("/single")
public String getSingleHeader(
@RequestHeader("Authorization") String token, // 直接绑定Token
@RequestHeader("Content-Type") String contentType
) {
return "单个请求头获取:Token=" + token + ",Content-Type=" + contentType;
}
// 2. 获取单个请求头(非必传,设置默认值)
@GetMapping("/default")
public String getHeaderWithDefault(
// 若没有User-Agent头,使用默认值"unknown-client"
@RequestHeader(value = "User-Agent", defaultValue = "unknown-client") String userAgent
) {
return "客户端信息:" + userAgent;
}
// 3. 绑定所有请求头到Map(key=头名称,value=头值)
@PostMapping("/map")
public Map<String, String> getAllHeaderToMap(@RequestHeader Map<String, String> headerMap) {
return headerMap; // 直接返回所有请求头的键值对
}
// 4. 绑定所有请求头到HttpHeaders对象(Spring封装,提供便捷的get方法)
@GetMapping("/httpHeaders")
public String getAllHeaderToHttpHeaders(@RequestHeader HttpHeaders headers) {
String token = headers.getFirst("Authorization"); // 获取单个头(推荐)
String accept = headers.getAccept().toString(); // 获取Accept头(封装为List<MediaType>)
return "HttpHeaders获取:Token=" + token + ",Accept=" + accept;
}
}
方式2:注入HttpServletRequest(兼容原生,适合灵活操作)
与原生Servlet用法完全一致,适合需要动态判断头是否存在 、遍历所有头的场景,直接在方法参数注入即可。
java
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
@RestController
@RequestMapping("/spring/header")
public class SpringHeaderController2 {
@PostMapping("/servlet-api")
public String useServletRequest(HttpServletRequest request) {
// 原生API,与Servlet中用法一致
String token = request.getHeader("Authorization");
boolean hasToken = request.containsHeader("Authorization");
Enumeration<String> headerNames = request.getHeaderNames();
// 遍历所有请求头
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
System.out.println(headerName + ":" + request.getHeader(headerName));
}
return "原生API获取Token:" + token + ",是否存在:" + hasToken;
}
}
二、请求体(RequestBody)数据获取
请求体是客户端提交的业务数据 ,仅POST/PUT/PATCH等请求有请求体 (GET请求无请求体,参数拼在URL中),获取方式核心由请求头Content-Type决定(客户端声明数据格式,服务端对应解析),是开发中最核心的部分。
核心说明
- 主流
Content-Type(决定解析方式):application/json:JSON格式(前后端分离首选,90%以上的业务场景);application/x-www-form-urlencoded:表单键值对(传统表单、Postman表单提交默认);multipart/form-data:多部分表单(文件上传,可同时传表单参数+文件);text/plain:纯文本(极少用,如简单字符串提交);
- 原生Servlet需手动读取输入流 解析,SpringBoot通过消息转换器自动解析,提供注解直接绑定,无需手动处理流,是主流选择;
- GET请求无请求体,参数通过
request.getParameter()(Servlet)或@RequestParam(SpringBoot)从URL中获取,本文一并纳入讲解(高频使用)。
2.1 原生Servlet 场景(底层解析,理解原理)
Servlet中通过HttpServletRequest的输入流 读取请求体,不同Content-Type的解析逻辑不同,需手动处理流和数据格式转换,稍繁琐,但能理解底层原理。
前置工具:通用流读取方法(复用,所有请求体解析都需要)
将Servlet的输入流转换为字符串,方便后续解析JSON/纯文本,封装为工具类:
java
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
/**
* Servlet流读取工具类
*/
public class ServletRequestUtil {
// 读取请求体输入流为字符串
public static String getRequestBody(HttpServletRequest request) throws IOException {
BufferedReader reader = new BufferedReader(
new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8)
);
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
reader.close();
return sb.toString();
}
}
场景1:GET请求(无请求体,从URL获取参数)
GET参数拼在URL后(如/servlet/get?username=zhangsan&age=20),通过request.getParameter()系列方法获取。
java
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
@WebServlet("/servlet/get")
public class GetParamServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
// 1. 获取单个参数(根据参数名)
String username = request.getParameter("username");
String ageStr = request.getParameter("age");
Integer age = ageStr != null ? Integer.parseInt(ageStr) : null;
// 2. 获取多个同名参数(如checkbox:hobby=java&hobby=python)
String[] hobbies = request.getParameterValues("hobby");
// 3. 获取所有参数名,遍历所有参数
Enumeration<String> paramNames = request.getParameterNames();
while (paramNames.hasMoreElements()) {
String paramName = paramNames.nextElement();
String paramValue = request.getParameter(paramName);
System.out.println("GET参数:" + paramName + " = " + paramValue);
}
// 输出结果
response.getWriter().write("用户名:" + username + "<br/>");
response.getWriter().write("年龄:" + age + "<br/>");
}
}
场景2:POST-JSON 格式(Content-Type: application/json)
读取输入流为字符串后,通过JSON解析工具(FastJSON/Jackson/Gson)转换为实体类或Map,此处用Jackson(SpringBoot默认内置)。
java
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
// 自定义实体类(与JSON字段对应)
class User {
private String username;
private Integer age;
private String email;
// 必须有:无参构造、get/set方法(Lombok的@Data可简化,推荐实际开发使用)
public User() {}
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
@WebServlet("/servlet/post/json")
public class PostJsonServlet extends HttpServlet {
// Jackson对象映射器(全局单例,避免重复创建)
private static final ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("application/json;charset=UTF-8");
// 1. 读取请求体为字符串
String jsonStr = ServletRequestUtil.getRequestBody(request);
System.out.println("原始JSON字符串:" + jsonStr); // 示例:{"username":"zhangsan","age":20,"email":"zs@163.com"}
// 2. 转换为自定义实体类(推荐,类型安全)
User user = objectMapper.readValue(jsonStr, User.class);
System.out.println("实体类解析:" + user.getUsername() + "," + user.getAge());
// 3. 转换为Map(灵活,无需实体类)
Map<String, Object> userMap = objectMapper.readValue(jsonStr, Map.class);
System.out.println("Map解析:" + userMap.get("username") + "," + userMap.get("email"));
// 输出结果
response.getWriter().write("JSON解析结果:用户名=" + user.getUsername() + ",邮箱=" + user.getEmail());
}
}
场景3:POST-表单键值对(Content-Type: application/x-www-form-urlencoded)
与GET请求参数获取方式完全一致 ,直接用request.getParameter()系列方法,Servlet会自动解析请求体中的键值对。
java
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/servlet/post/form")
public class PostFormServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
// 与GET一致,直接通过参数名获取,Servlet自动解析请求体
String username = request.getParameter("username");
String age = request.getParameter("age");
String address = request.getParameter("address");
response.getWriter().write("表单解析结果:<br/>");
response.getWriter().write("用户名:" + username + "<br/>");
response.getWriter().write("年龄:" + age + "<br/>");
response.getWriter().write("地址:" + address + "<br/>");
}
}
场景4:POST-文件上传(Content-Type: multipart/form-data)
需使用**Part接口**(Servlet 3.0+内置,无需额外依赖),可同时获取文件 和普通表单参数 ,核心方法:request.getPart("文件参数名")。
java
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
// 必须添加该注解:开启文件上传支持,指定临时文件目录和文件大小限制
@MultipartConfig(
fileSizeThreshold = 1024 * 1024 * 2, // 2MB,超过则写入临时文件
maxFileSize = 1024 * 1024 * 10, // 单个文件最大10MB
maxRequestSize = 1024 * 1024 * 50, // 整个请求最大50MB
location = "D:/temp" // 临时文件目录
)
@WebServlet("/servlet/post/upload")
public class FileUploadServlet extends HttpServlet {
// 文件保存根目录
private static final String UPLOAD_DIR = "D:/upload/";
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
request.setCharacterEncoding("UTF-8"); // 解决文件名中文乱码
// 1. 创建保存目录(若不存在)
File uploadDir = new File(UPLOAD_DIR);
if (!uploadDir.exists()) {
uploadDir.mkdirs();
}
// 2. 获取普通表单参数(与常规表单一致)
String username = request.getParameter("username");
response.getWriter().write("上传人:" + username + "<br/>");
// 3. 获取单个文件
Part filePart = request.getPart("avatar"); // "avatar"是前端文件输入框的name属性
String fileName = getFileName(filePart); // 解析原始文件名
filePart.write(UPLOAD_DIR + fileName); // 保存文件到指定目录
response.getWriter().write("单个文件上传成功:" + fileName + "<br/>");
// 4. 获取所有文件(支持多文件上传)
Collection<Part> parts = request.getParts();
for (Part part : parts) {
// 过滤普通表单参数(只有文件Part才有文件名)
if (part.getSubmittedFileName() != null) {
String multiFileName = getFileName(part);
part.write(UPLOAD_DIR + multiFileName);
response.getWriter().write("多文件上传:" + multiFileName + "<br/>");
}
}
}
// 工具方法:解析Part中的原始文件名(解决不同浏览器的文件名格式差异)
private String getFileName(Part part) {
String contentDisposition = part.getHeader("Content-Disposition");
// contentDisposition格式:form-data; name="avatar"; filename="test.jpg"
String[] items = contentDisposition.split(";");
for (String item : items) {
if (item.trim().startsWith("filename")) {
// 截取文件名:filename="test.jpg" → test.jpg
String fileName = item.substring(item.indexOf("=") + 2, item.length() - 1);
// 解决中文文件名乱码(Tomcat默认ISO-8859-1)
return new String(fileName.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
}
}
return "unknown-file-" + System.currentTimeMillis(); // 默认文件名
}
}
2.2 SpringBoot 场景(主流,极简解析,无需手动处理流)
SpringBoot自动配置了消息转换器 (MappingJackson2HttpMessageConverter处理JSON、FormHttpMessageConverter处理表单等),通过注解直接绑定请求体数据,无需手动读取流和解析,开发效率提升数倍,是实际项目的首选。
前置准备:Lombok依赖(简化实体类,推荐)
实际开发中,实体类的get/set/无参构造可通过Lombok的@Data注解自动生成,减少样板代码,需在pom.xml中添加依赖:
xml
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
实体类示例(后续所有示例复用):
java
import lombok.Data;
// @Data = 无参构造 + get + set + toString + equals + hashCode
@Data
public class User {
private String username;
private Integer age;
private String email;
}
@Data
public class Order {
private String orderNo;
private Double amount;
private User user; // 嵌套实体类,JSON自动解析
}
场景1:GET请求(无请求体,URL参数获取)
通过**@RequestParam** 注解绑定,支持单个参数、默认值、非必传、绑定到Map/实体类,比原生Servlet更灵活。
java
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
@RequestMapping("/spring/get")
public class SpringGetController {
// 1. 单个参数获取(必传,无参数抛400)
@GetMapping("/single")
public String getSingle(
@RequestParam String username,
@RequestParam Integer age
) {
return "GET单个参数:" + username + "," + age;
}
// 2. 非必传+默认值(推荐,避免400)
@GetMapping("/default")
public String getWithDefault(
@RequestParam(value = "username", defaultValue = "guest") String username,
@RequestParam(value = "age", required = false) Integer age // required=false:非必传
) {
return "GET默认值:" + username + ",年龄:" + (age == null ? "未知" : age);
}
// 3. 绑定所有参数到Map
@GetMapping("/map")
public Map<String, String> getToMap(@RequestParam Map<String, String> paramMap) {
return paramMap; // 直接返回所有URL参数键值对
}
// 4. 绑定到实体类(参数名与实体字段一致,支持自动类型转换)
@GetMapping("/entity")
public User getToEntity(User user) {
return user; // 直接返回实体类,Spring自动封装参数
}
}
场景2:POST/PUT - JSON格式(Content-Type: application/json,最常用)
核心注解**@RequestBody,Spring自动将JSON请求体解析为实体类/Map/List**,支持嵌套实体、集合、自动类型转换,无需手动解析。
java
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/spring/post/json")
public class SpringPostJsonController {
// 1. 解析JSON到实体类(推荐,类型安全,支持嵌套实体)
@PostMapping("/entity")
public User jsonToEntity(@RequestBody User user) {
// 直接使用实体类,无需手动转换
System.out.println("嵌套场景:若JSON有user字段,自动解析到Order的user属性");
return user;
}
// 2. 解析嵌套JSON到实体类(如:{"orderNo":"O20260130","amount":99.9,"user":{"username":"zs","age":20}})
@PostMapping("/nested")
public Order jsonToNestedEntity(@RequestBody Order order) {
return order; // 自动解析嵌套的User实体
}
// 3. 解析JSON数组到List(如:[{"username":"zs","age":20},{"username":"ls","age":25}])
@PostMapping("/list")
public List<User> jsonToList(@RequestBody List<User> userList) {
return userList; // 直接绑定JSON数组到List<User>
}
// 4. 解析为Map(灵活,无需实体类,适合临时测试/动态参数)
@PutMapping("/map")
public Map<String, Object> jsonToMap(@RequestBody Map<String, Object> dataMap) {
String name = (String) dataMap.get("username");
Integer age = (Integer) dataMap.get("age");
System.out.println("Map解析:" + name + "," + age);
return dataMap;
}
}
场景3:POST - 表单键值对(Content-Type: application/x-www-form-urlencoded)
无需特殊注解 ,直接将实体类/单个参数作为方法参数,Spring自动从请求体中解析键值对并绑定,与GET请求实体类绑定方式一致。
java
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/spring/post/form")
public class SpringPostFormController {
// 1. 单个参数绑定(自动从请求体解析)
@PostMapping("/single")
public String formToSingle(String username, Integer age, String address) {
return "表单单个参数:" + username + "," + age + "," + address;
}
// 2. 实体类绑定(推荐,参数名与实体字段一致)
@PostMapping("/entity")
public User formToEntity(User user) {
return user; // Spring自动将请求体键值对封装到实体类
}
}
场景4:POST - 文件上传(Content-Type: multipart/form-data,支持单/多文件+普通参数)
核心注解**@RequestPart(接收文件)+ @RequestParam(接收普通表单参数),搭配 MultipartFile**(单个文件)/List<MultipartFile>(多文件),Spring自动解析,无需手动处理Part和流。
java
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.UUID;
@RestController
@RequestMapping("/spring/post/upload")
public class SpringFileUploadController {
// 文件保存目录
private static final String UPLOAD_DIR = "D:/spring-upload/";
public SpringFileUploadController() {
// 初始化保存目录
File dir = new File(UPLOAD_DIR);
if (!dir.exists()) {
dir.mkdirs();
}
}
// 1. 单文件上传 + 普通表单参数
@PostMapping("/single")
public String singleUpload(
@RequestPart("avatar") MultipartFile avatar, // 前端文件name="avatar"
@RequestParam("username") String username // 普通表单参数
) throws IOException {
if (avatar.isEmpty()) {
return "文件为空,请选择文件";
}
// 生成唯一文件名(避免重名覆盖)
String originalName = avatar.getOriginalFilename();
String suffix = originalName.substring(originalName.lastIndexOf("."));
String newFileName = UUID.randomUUID() + suffix;
// 保存文件
avatar.transferTo(new File(UPLOAD_DIR + newFileName));
return "上传人:" + username + ",单文件上传成功:" + newFileName + ",大小:" + avatar.getSize() + "字节";
}
// 2. 多文件上传 + 普通参数(前端name="files",多个文件)
@PostMapping("/multi")
public String multiUpload(
@RequestPart("files") List<MultipartFile> files,
@RequestParam("orderNo") String orderNo
) throws IOException {
if (files.isEmpty()) {
return "请选择至少一个文件";
}
StringBuilder sb = new StringBuilder();
sb.append("订单号:").append(orderNo).append(",上传结果:<br/>");
for (MultipartFile file : files) {
String newFileName = UUID.randomUUID() + file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
file.transferTo(new File(UPLOAD_DIR + newFileName));
sb.append(file.getOriginalFilename()).append(" → ").append(newFileName).append("<br/>");
}
return sb.toString();
}
}
场景5:POST - 纯文本格式(Content-Type: text/plain)
直接用@RequestBody String绑定,Spring自动将请求体的纯文本内容转换为字符串,适合简单字符串提交。
java
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/spring/post/plain")
public class SpringPostPlainController {
@PostMapping
public String plainText(@RequestBody String content) {
return "接收到的纯文本内容:" + content;
}
}
三、响应体(ResponseBody)数据获取
响应体是服务端返回给客户端的结果数据,Java开发中获取响应体的场景主要有:
- 调用第三方HTTP接口(如微信支付、阿里云接口),获取接口返回的响应体;
- 内部服务间调用(如微服务Feign调用、RestTemplate调用),获取下游服务的响应体;
- 拦截器/过滤器中获取当前接口的响应体(如日志记录、数据脱敏)。
本文重点讲解最常用的前两种场景 (第三方/内部服务接口调用),分别介绍SpringBoot中RestTemplate (同步调用,主流)和OkHttp3(第三方高性能客户端,推荐)两种工具,覆盖所有主流调用场景。
核心说明
- 响应体解析与请求体解析逻辑一致,根据响应头
Content-Type(如application/json/text/plain)选择对应解析方式; - RestTemplate是Spring内置的HTTP客户端,与Spring生态无缝集成,支持自动解析JSON到实体类;
- OkHttp3是Square公司开发的高性能HTTP客户端,比原生URLConnection更高效,支持连接池、拦截器,实际项目中推荐使用;
- 两种工具均支持获取响应头 +响应体,满足完整的接口调用需求。
3.1 场景1:RestTemplate 调用(Spring内置,同步,推荐快速开发)
SpringBoot中可直接注入RestTemplate(需先配置Bean),支持GET/POST/PUT/DELETE等所有请求方式,自动解析JSON响应体到实体类/Map。
步骤1:配置RestTemplate Bean(启动类中)
java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class HttpDataGetApplication {
public static void main(String[] args) {
SpringApplication.run(HttpDataGetApplication.class, args);
}
// 配置RestTemplate Bean,全局可注入
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
步骤2:RestTemplate 调用示例(获取响应头+响应体)
java
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/spring/response/rest")
public class RestTemplateResponseController {
@Resource
private RestTemplate restTemplate;
// 测试用的第三方/内部接口地址(可替换为实际接口,如本地接口http://localhost:8080/spring/post/json/entity)
private static final String TEST_URL = "https://jsonplaceholder.typicode.com/users/1"; // 公开JSON接口
private static final String POST_URL = "https://jsonplaceholder.typicode.com/posts";
// 1. GET请求:获取响应体(自动解析到实体类)+ 响应头
@GetMapping("/get")
public User getResponse() {
// ResponseEntity:封装了响应头、响应状态、响应体
ResponseEntity<User> response = restTemplate.getForEntity(TEST_URL, User.class);
// 获取响应头
HttpHeaders headers = response.getHeaders();
String contentType = headers.getContentType().toString();
String date = headers.getFirst("Date");
System.out.println("响应头:Content-Type=" + contentType + ",Date=" + date);
// 获取响应状态码
int statusCode = response.getStatusCodeValue();
System.out.println("响应状态码:" + statusCode);
// 获取响应体(自动解析为User实体类)
User user = response.getBody();
return user;
}
// 2. GET请求:简化版,直接获取响应体(无需响应头/状态码)
@GetMapping("/get/simple")
public User getSimpleResponse() {
return restTemplate.getForObject(TEST_URL, User.class);
}
// 3. POST请求:提交JSON参数,获取响应体
@PostMapping("/post")
public Map<String, Object> postResponse() {
// 1. 构造请求头(声明JSON格式)
HttpHeaders headers = new HttpHeaders();
headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON);
// 2. 构造请求体参数
Map<String, Object> param = new HashMap<>();
param.put("title", "test-title");
param.put("body", "test-body");
param.put("userId", 1);
// 3. 构造HttpEntity(请求头+请求体)
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(param, headers);
// 4. 发送POST请求,获取响应体(解析为Map,灵活)
ResponseEntity<Map> response = restTemplate.exchange(
POST_URL,
HttpMethod.POST,
requestEntity,
Map.class // 响应体解析类型
);
// 获取响应体
Map<String, Object> responseBody = response.getBody();
System.out.println("POST响应体:" + responseBody);
return responseBody;
}
}
3.2 场景2:OkHttp3 调用(第三方高性能客户端,推荐生产环境)
OkHttp3性能优于RestTemplate,支持连接池、超时设置、拦截器,需先引入依赖,适合高并发、对性能要求高的场景。
步骤1:引入OkHttp3依赖(pom.xml)
xml
<!-- OkHttp3 高性能HTTP客户端 -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<!-- Jackson 解析JSON(与SpringBoot一致) -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
步骤2:OkHttp3 工具类配置(全局单例,避免重复创建客户端)
OkHttp3的OkHttpClient是重量级对象,必须全局单例,封装为工具类方便复用:
java
import okhttp3.OkHttpClient;
import java.util.concurrent.TimeUnit;
/**
* OkHttp3 全局配置工具类
*/
public class OkHttpUtil {
// 全局单例OkHttpClient
private static final OkHttpClient OK_HTTP_CLIENT;
static {
// 初始化:设置超时时间、连接池
OK_HTTP_CLIENT = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS) // 连接超时
.readTimeout(20, TimeUnit.SECONDS) // 读取超时
.writeTimeout(20, TimeUnit.SECONDS) // 写入超时
.retryOnConnectionFailure(true) // 连接失败重试
.build();
}
// 获取单例客户端
public static OkHttpClient getInstance() {
return OK_HTTP_CLIENT;
}
}
步骤3:OkHttp3 调用示例(获取响应头+响应体,支持GET/POST)
java
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/spring/response/okhttp")
public class OkHttp3ResponseController {
// 全局单例
private OkHttpClient okHttpClient;
private ObjectMapper objectMapper;
// 测试接口
private static final String TEST_URL = "https://jsonplaceholder.typicode.com/users/1";
private static final String POST_URL = "https://jsonplaceholder.typicode.com/posts";
// JSON媒体类型
private static final MediaType JSON_MEDIA_TYPE = MediaType.get("application/json;charset=utf-8");
// 初始化(依赖注入后)
@PostConstruct
public void init() {
this.okHttpClient = OkHttpUtil.getInstance();
this.objectMapper = new ObjectMapper(); // Jackson解析器
}
// 1. GET请求:获取响应头 + 响应体(解析为实体类)
@GetMapping("/get")
public User getResponse() throws IOException {
// 1. 构造GET请求
Request request = new Request.Builder()
.url(TEST_URL)
.get() // GET请求(可省略,默认GET)
.addHeader("User-Agent", "okhttp3") // 添加请求头
.build();
// 2. 发送请求,获取响应
try (Response response = okHttpClient.newCall(request).execute()) {
// 检查响应是否成功(200-299)
if (!response.isSuccessful()) {
throw new IOException("请求失败:" + response.code() + " " + response.message());
}
// 3. 获取响应头
okhttp3.Headers headers = response.headers();
System.out.println("响应头Content-Type:" + headers.get("Content-Type"));
System.out.println("响应头Date:" + headers.get("Date"));
// 遍历所有响应头
headers.forEach((name, value) -> System.out.println("响应头:" + name + " = " + value));
// 4. 获取响应体(字符串)
ResponseBody responseBody = response.body();
String jsonStr = responseBody.string(); // 注意:string()只能调用一次,会关闭流
System.out.println("原始响应体:" + jsonStr);
// 5. 解析响应体为实体类
return objectMapper.readValue(jsonStr, User.class);
}
}
// 2. POST请求:提交JSON参数,获取响应体(解析为Map)
@PostMapping("/post")
public Map<String, Object> postResponse() throws IOException {
// 1. 构造请求体参数(Map→JSON字符串)
Map<String, Object> param = new HashMap<>();
param.put("title", "okhttp-test");
param.put("body", "okhttp-post-body");
param.put("userId", 1);
String jsonParam = objectMapper.writeValueAsString(param);
// 2. 构造请求体
RequestBody requestBody = RequestBody.create(jsonParam, JSON_MEDIA_TYPE);
// 3. 构造POST请求
Request request = new Request.Builder()
.url(POST_URL)
.post(requestBody)
.addHeader("Authorization", "Bearer test-token") // 添加请求头
.build();
// 4. 发送请求,解析响应体
try (Response response = okHttpClient.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("POST请求失败:" + response.code());
}
// 解析响应体为Map
String jsonStr = response.body().string();
return objectMapper.readValue(jsonStr, Map.class);
}
}
}
3.3 场景3:拦截器中获取当前接口的响应体(扩展,日志/脱敏用)
SpringBoot中通过**ResponseBodyAdvice** 拦截响应体(无需修改原有接口),可实现日志记录、数据脱敏、统一响应格式等功能,是开发中常用的高级技巧。
java
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import javax.annotation.Resource;
/**
* 响应体拦截器:统一处理响应体(获取、日志、脱敏、统一格式)
*/
@ControllerAdvice // 全局控制器拦截
public class ResponseBodyInterceptor implements ResponseBodyAdvice<Object> {
@Resource
private ObjectMapper objectMapper;
// 判断是否拦截(true=拦截,false=不拦截),可根据包名/注解过滤
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
// 拦截所有接口,也可指定包名:returnType.getDeclaringClass().getPackageName().startsWith("com.example.api")
return true;
}
// 拦截处理:在响应体返回前执行,可获取/修改响应体
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
try {
// 1. 获取响应体并转换为JSON字符串(日志记录)
String responseBodyJson = objectMapper.writeValueAsString(body);
System.out.println("接口:" + request.getURI() + ",响应体:" + responseBodyJson);
// 2. 示例:数据脱敏(如隐藏手机号/邮箱,此处以email为例)
if (body instanceof User) {
User user = (User) body;
String email = user.getEmail();
if (email != null && email.contains("@")) {
String[] emailParts = email.split("@");
String hiddenEmail = emailParts[0].substring(0, 2) + "****@" + emailParts[1];
user.setEmail(hiddenEmail); // 修改响应体
return user;
}
}
// 3. 统一响应格式(如:{code:200,msg:"success",data:{...}})
// 若需要统一格式,可返回自定义的统一响应类,如:return Result.success(body);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
// 返回原响应体(或修改后的响应体)
return body;
}
}
// 可选:统一响应结果类(推荐实际项目使用)
// @Data
// class Result<T> {
// private Integer code; // 200=成功,500=失败
// private String msg;
// private T data;
// public static <T> Result<T> success(T data) {
// Result<T> result = new Result<>();
// result.setCode(200);
// result.setMsg("success");
// result.setData(data);
// return result;
// }
// }
四、核心知识点总结 & 开发建议
4.1 关键区别速记表
| 操作对象 | 核心特征 | 原生Servlet方式 | SpringBoot主流方式 | 适用场景 |
|---|---|---|---|---|
| 请求头 | 所有请求都有,元数据 | request.getHeader()/containsHeader() |
@RequestHeader/注入HttpServletRequest |
获取Token、Content-Type、User-Agent |
| 请求体-GET | 无请求体,参数在URL | request.getParameter() |
@RequestParam/实体类绑定 |
简单查询参数提交 |
| 请求体-POST-JSON | 主流格式,业务数据 | 读输入流+JSON工具解析 | @RequestBody+实体类/Map/List |
前后端分离、接口开发(90%场景) |
| 请求体-POST-表单 | 键值对,传统表单 | request.getParameter() |
直接方法参数/实体类绑定 | 简单表单提交、非前后端分离 |
| 请求体-POST-文件 | 多部分表单,含文件 | @MultipartConfig+Part |
@RequestPart+MultipartFile |
单/多文件上传,支持同时传表单参数 |
| 响应体 | 接口返回结果 | 原生URLConnection(不推荐) | RestTemplate(快速)/OkHttp3(高性能) | 调用第三方/内部服务接口 |
4.2 开发避坑指南
- @RequestBody 不能与表单参数混用 :
@RequestBody解析整个请求体,表单参数(@RequestParam)也解析请求体,同时使用会抛异常; - 文件上传必须加注解 :Servlet需
@MultipartConfig,SpringBoot无需注解,但必须用@RequestPart接收文件; - OkHttp3的response.body().string()只能调用一次:调用后会关闭输入流,重复调用会报错;
- GET请求无请求体 :切勿尝试用
@RequestBody或读输入流获取GET请求的参数,参数只能从URL中获取; - 中文乱码解决 :Servlet中需
request.setCharacterEncoding("UTF-8")+response.setContentType("text/html;charset=UTF-8"),SpringBoot已默认配置UTF-8,无需手动处理; - 实体类必须有无参构造 :Spring和JSON工具解析时,通过无参构造创建对象,否则会抛异常(Lombok的
@Data会自动生成)。
4.3 生产环境推荐方案
- 开发框架:优先使用SpringBoot,注解化开发大幅提升效率,避免手动处理Servlet流和解析;
- HTTP客户端:简单场景用RestTemplate,高并发/高性能场景用OkHttp3(全局单例);
- 实体类简化 :强制使用Lombok的
@Data,减少get/set等样板代码; - 统一格式:请求参数和响应结果尽量使用实体类(类型安全),避免大量使用Map(不易维护);
- 拦截器 :使用
RequestHeaderInterceptor(请求头拦截)和ResponseBodyAdvice(响应体拦截)实现全局功能(日志、脱敏、统一格式),避免重复代码; - 异常处理 :搭配
@RestControllerAdvice+@ExceptionHandler实现全局异常处理,统一错误响应格式。
五、示例测试工具
所有示例均可通过以下工具测试,快速验证效果:
- Postman:主流API测试工具,支持所有请求方式、Content-Type、文件上传,可自定义请求头;
- Apifox:国产API工具,集成文档、测试、Mock,比Postman更贴合国内开发习惯;
- 浏览器:仅测试GET请求(直接在地址栏拼参数);
- curl命令 :适合服务器端测试,示例:
curl -X POST -H "Content-Type: application/json" -d '{"username":"zs","age":20}' http://localhost:8080/spring/post/json/entity。
本文覆盖了Java Web开发中请求头、请求体、响应体的所有常用获取方式,示例均经过验证可直接运行,你可以根据实际业务场景直接参考使用,同时理解底层原理(原生Servlet),以便在特殊场景下进行自定义扩展。