如何在客户端应用内拦截并获取HTTP请求的完整流量?
在Java应用内获取HttpURLConnection发送的完整HTTP请求内容
要在应用内捕获从请求行到最后一个字节的完整HTTP请求内容,有两种可行方案,分别适配原生HttpURLConnection和更易用的OkHttp客户端:
方案一:自定义HttpURLConnection子类拦截请求
原生HttpURLConnection会自动构建请求行和头信息,仅包装输出流只能拿到请求体,因此需要自定义子类来捕获完整请求数据:
1. 实现请求体追踪输出流
用于记录写入的请求体字节:
import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; public class TrackingOutputStream extends OutputStream { private final OutputStream delegate; private final ByteArrayOutputStream buffer; public TrackingOutputStream(OutputStream delegate) { this.delegate = delegate; this.buffer = new ByteArrayOutputStream(); } @Override public void write(int b) throws IOException { delegate.write(b); buffer.write(b); } @Override public void write(byte[] b) throws IOException { delegate.write(b); buffer.write(b); } @Override public void write(byte[] b, int off, int len) throws IOException { delegate.write(b, off, len); buffer.write(b, off, len); } public byte[] getCapturedBytes() { return buffer.toByteArray(); } }
2. 自定义HttpURLConnection与URL处理器
拦截请求构建过程,拼接请求行、头和体:
import java.io.IOException; import java.io.OutputStream; import java.net.*; import java.nio.charset.StandardCharsets; import java.util.Map; public class TrackingHttpURLConnection extends HttpURLConnection { private final HttpURLConnection delegate; private TrackingOutputStream trackingOutputStream; private String fullRequest; protected TrackingHttpURLConnection(URL url, HttpURLConnection delegate) { super(url); this.delegate = delegate; } @Override public void connect() throws IOException { // 构建请求行 String requestLine = String.format("%s %s HTTP/1.1\r\n", getRequestMethod(), getURL().getPath()); // 构建请求头 StringBuilder headersBuilder = new StringBuilder(); for (Map.Entry<String, String> entry : delegate.getRequestProperties().entrySet()) { headersBuilder.append(String.format("%s: %s\r\n", entry.getKey(), entry.getValue())); } headersBuilder.append("\r\n"); // 头与体的分隔空行 delegate.connect(); // 拼接完整请求 if (trackingOutputStream != null) { byte[] bodyBytes = trackingOutputStream.getCapturedBytes(); fullRequest = requestLine + headersBuilder + new String(bodyBytes, StandardCharsets.UTF_8); } else { fullRequest = requestLine + headersBuilder; } } @Override public OutputStream getOutputStream() throws IOException { if (trackingOutputStream == null) { trackingOutputStream = new TrackingOutputStream(delegate.getOutputStream()); } return trackingOutputStream; } // 委托必要的原生方法 @Override public int getResponseCode() throws IOException { return delegate.getResponseCode(); } @Override public InputStream getInputStream() throws IOException { return delegate.getInputStream(); } @Override public void setRequestMethod(String method) throws ProtocolException { delegate.setRequestMethod(method); } @Override public void setDoOutput(boolean dooutput) { delegate.setDoOutput(dooutput); } // 获取完整请求内容的方法 public String getFullRequest() { return fullRequest; } @Override public void disconnect() { delegate.disconnect(); } @Override public boolean usingProxy() { return delegate.usingProxy(); } } // 自定义URL处理器工厂 class TrackingURLStreamHandlerFactory implements URLStreamHandlerFactory { @Override public URLStreamHandler createURLStreamHandler(String protocol) { if ("http".equals(protocol)) { return new URLStreamHandler() { @Override protected URLConnection openConnection(URL url) throws IOException { HttpURLConnection originalConn = (HttpURLConnection) url.openConnection(); return new TrackingHttpURLConnection(url, originalConn); } }; } return null; // 其他协议用默认实现 } }
3. 使用示例
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.nio.charset.StandardCharsets; public class Main { public static void main(String[] args) throws IOException { // 注册自定义URL处理器 URL.setURLStreamHandlerFactory(new TrackingURLStreamHandlerFactory()); URL url = new URL("http://localhost/test"); TrackingHttpURLConnection http = (TrackingHttpURLConnection) url.openConnection(); http.setRequestMethod("POST"); http.setDoOutput(true); http.setRequestProperty("Content-Type", "text/plain; charset=utf-8"); OutputStream os = http.getOutputStream(); os.write("test".getBytes(StandardCharsets.UTF_8)); os.flush(); os.close(); InputStream is = http.getInputStream(); byte[] resp = is.readAllBytes(); is.close(); // 获取并使用完整请求内容 String fullRequest = http.getFullRequest(); System.out.println("完整请求内容:"); System.out.println(fullRequest); } }
方案二:改用OkHttp客户端(更简洁)
如果可以替换原生客户端,OkHttp的拦截器机制能更轻松捕获完整请求:
1. 添加依赖(Maven)
<dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>4.12.0</version> </dependency>
2. 拦截器实现示例
import okhttp3.*; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Map; public class OkHttpTrackingExample { public static void main(String[] args) throws IOException { // 自定义拦截器捕获完整请求 Interceptor trackingInterceptor = chain -> { Request request = chain.request(); // 读取请求体 String requestBody = null; if (request.body() != null) { Buffer buffer = new Buffer(); request.body().writeTo(buffer); requestBody = buffer.readString(StandardCharsets.UTF_8); } // 拼接完整请求内容 StringBuilder fullRequest = new StringBuilder(); fullRequest.append(String.format("%s %s HTTP/1.1\r\n", request.method(), request.url().encodedPath())); // 写入请求头 for (Map.Entry<String, String> entry : request.headers().toMultimap().entrySet()) { for (String value : entry.getValue()) { fullRequest.append(String.format("%s: %s\r\n", entry.getKey(), value)); } } fullRequest.append("\r\n"); if (requestBody != null) { fullRequest.append(requestBody); } // 日志/归档逻辑 System.out.println("完整请求内容:"); System.out.println(fullRequest); // 继续执行请求 return chain.proceed(request); }; OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(trackingInterceptor) .build(); RequestBody body = RequestBody.create("test".getBytes(StandardCharsets.UTF_8), MediaType.parse("text/plain")); Request request = new Request.Builder() .url("http://localhost/test") .post(body) .addHeader("Content-Type", "text/plain; charset=utf-8") .build(); try (Response response = client.newCall(request).execute()) { // 处理响应 String respBody = response.body().string(); } } }
注意事项
- 方案一中需确保自定义
HttpURLConnection委托所有必要的原生方法,避免功能缺失 - 若请求体为二进制数据,建议直接存储字节数组而非转字符串,避免编码异常
- 方案二更适合新项目或可替换客户端的场景,代码维护成本更低
内容的提问来源于stack exchange,提问作者chris01
相关产品推荐
相关产品推荐

