Java中如何通过HTTP POST请求发送另一HTTP请求完整内容
问题根因
当前方案失败的核心原因有两个:
java.net.http.HttpRequest从API设计上就没有实现Serializable接口,其内部依赖的BodyPublisher等组件同样不支持Java原生序列化,直接用ObjectOutputStream写入必然抛出序列化异常。- 就算通过反射等黑科技强行做Java原生序列化,生成的二进制流是JDK版本强绑定的,接收端只要JDK版本不一致、或者没有对应类路径,就完全无法解析,根本不适合跨HTTP传输。
另外注意,JDK的HttpRequest实例没有提供公开的API读取已经传入的请求体内容,就算想反射抠内容,也要面对不同JDK版本内部字段名变动的问题,维护成本极高。
可行实现方案
不要尝试直接序列化原生HttpRequest对象,最通用、兼容性最好的方式是在构建原始请求前,就把请求的所有必要参数单独留存,组装成通用的结构化数据结构,序列化后作为外层请求的请求体传输,由于外层请求本身用的是application/json类型,直接用JSON做序列化格式是成本最低的选择。
具体实现步骤:
- 定义一个普通的可序列化DTO类,用来承载原始请求的全量信息,包括请求方法、目标URI、请求头集合、请求体内容。
- 构建原始HttpRequest之前,先把所有请求参数存在上述DTO对象里,再用同样的参数构建需要的原始HttpRequest实例(如果后续还要发原始请求的话)。
- 用通用JSON序列化工具(Jackson、Gson都可以)把DTO序列化成字节数组,作为外层POST请求的请求体发送。
- 接收端拿到请求体后反序列化为同一个DTO类,就能拿到原始请求的所有信息,按需还原请求即可。
代码示例
首先定义承载请求信息的DTO:
import java.io.Serializable; import java.util.List; import java.util.Map; // 承载原始HTTP请求全量信息的传输对象 public class ForwardRequestDTO implements Serializable { private String method; private String targetUri; private Map<String, List<String>> headers; private String requestBody; // 如果传输二进制请求体,把类型改为byte[]即可 // 全字段getter、setter、无参构造 public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public String getTargetUri() { return targetUri; } public void setTargetUri(String targetUri) { this.targetUri = targetUri; } public Map<String, List<String>> getHeaders() { return headers; } public void setHeaders(Map<String, List<String>> headers) { this.headers = headers; } public String getRequestBody() { return requestBody; } public void setRequestBody(String requestBody) { this.requestBody = requestBody; } }
然后是请求构建与发送的实现:
import com.fasterxml.jackson.databind.ObjectMapper; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; public class RequestForwardDemo { private static final HttpClient httpClient = HttpClient.newHttpClient(); private static final ObjectMapper objectMapper = new ObjectMapper(); private static final String baseUri = "http://your-service-domain"; public static void main(String[] args) throws Exception { // 1. 提前留存原始请求的所有参数,不要等build完HttpRequest再尝试提取 String originalTarget = baseUri + "/Request"; String originalMethod = "POST"; Map<String, List<String>> originalHeaders = Map.of( "Content-Type", List.of("application/json") ); String originalBody = "Example of body"; // 2. 正常构建需要的原始HttpRequest(如果后续需要直接发原始请求就用这个对象) HttpRequest originalRequest = HttpRequest.newBuilder() .uri(URI.create(originalTarget)) .setHeader("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(originalBody)) .build(); // 3. 组装转发用的DTO ForwardRequestDTO forwardPayload = new ForwardRequestDTO(); forwardPayload.setMethod(originalMethod); forwardPayload.setTargetUri(originalTarget); forwardPayload.setHeaders(originalHeaders); forwardPayload.setRequestBody(originalBody); // 4. 序列化为JSON字节数组作为外层请求体 byte[] sendBytes = objectMapper.writeValueAsBytes(forwardPayload); // 5. 构建外层转发请求,注意外层接口路径不要和原始请求路径重复,避免逻辑错误 HttpRequest forwardRequest = HttpRequest.newBuilder() .uri(URI.create(baseUri + "/ForwardRequest")) .setHeader("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofByteArray(sendBytes)) .build(); // 6. 发送请求 HttpResponse<byte[]> response = httpClient.send(forwardRequest, HttpResponse.BodyHandlers.ofByteArray()); System.out.println("响应状态码:" + response.statusCode()); System.out.println("响应内容:" + new String(response.body(), StandardCharsets.UTF_8)); } }
注意事项:
- 不要在生产环境用Java原生序列化做跨网络传输,不仅版本兼容性极差,还存在已知的反序列化安全漏洞。
- 如果原始请求是文件上传这类带二进制请求体的场景,把DTO里的
requestBody字段类型改为byte[]即可,JSON序列化工具会自动做Base64编码适配文本传输。- 多值请求头直接用
Map<String, List<String>>结构存储即可,不会丢失头信息。
内容的提问来源于stack exchange,提问作者Slaymut
相关产品推荐
相关产品推荐

