You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在客户端应用内拦截并获取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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 10:24:52