Java常用HTTP请求类库与HttpClient

Apache HttpClient 与 JDK 11+ HttpClient 完全无关,需通过 import 区分;Apache 版本发 POST 表单须手动设 Content-Type;JDK 版本不支持 HTTP/1.0 且默认无连接池;OkHttp 轻量但异常易被拦截器掩盖。

HttpClient 是 Apache 提供的底层 HTTP 客户端,不是 Java 标准库自带的

Java 11+ 虽然引入了 java.net.http.HttpClient,但很多人说的 “HttpClient” 实际指 org.apache.http.client.HttpClient(即 Apache HttpClient 4.x)。两者完全无关,包名、API 设计、生命周期管理都不同,混用会导致编译失败或运行时 NoClassDefFoundError

常见误判场景:

  • 看到项目里有 HttpClient client = HttpClients.createDefault(),就以为是 JDK 自带 —— 其实是 Apache 的
  • Maven 依赖写成 org.apache.h

    ttpcomponents:httpclient
    却期望它支持 HttpRequest.newBuilder() —— 这是 JDK 11+ 的 API

确认方式:看 import 语句。Apache 版本 import 以 org.apache.http 开头;JDK 原生版 import 是 java.net.http

Apache HttpClient 4.x 发送 POST 表单必须显式设置 Content-Type

UrlEncodedFormEntity 构建表单参数时,HttpClient 不会自动加 Content-Type: application/x-www-form-urlencoded,漏掉会导致服务端收不到参数(尤其 Spring Boot 接口返回 400 或空 body)。

正确写法要点:

  • 创建 UrlEncodedFormEntity 后,手动调用 entity.setContentType("application/x-www-form-urlencoded; charset=UTF-8")
  • 或改用 UnmodifiableList.of(new BasicNameValuePair("k", "v")) + new UrlEncodedFormEntity(...),再 set 到 HttpPost
  • 避免直接传字符串 body 并手动 setHeader,那样容易编码错乱
HttpPost post = new HttpPost("https://api.example.com/login");
List params = Arrays.asList(
    new BasicNameValuePair("username", "admin"),
    new BasicNameValuePair("password", "123")
);
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, StandardCharsets.UTF_8);
entity.setContentType("application/x-www-form-urlencoded; charset=UTF-8"); // 必须加
post.setEntity(entity);

JDK 11+ HttpClient 默认不支持 HTTP/1.0,且连接不复用

java.net.http.HttpClient 默认只协商 HTTP/1.1 或 HTTP/2,遇到只支持 HTTP/1.0 的老旧服务(如某些嵌入式设备接口),会直接抛 java.net.http.HttpTimeoutException 或连接重置。

另外,它的默认实例(HttpClient.newBuilder().build())不开启连接池,每个请求新建 TCP 连接,高并发下性能明显差于 Apache HttpClient 的 PoolingHttpClientConnectionManager

优化建议:

  • 对接老系统时,改用 HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_0).build()
  • 生产环境务必复用客户端实例,并配置连接超时:HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build()
  • 如需连接池、重试、响应缓存等能力,Apache HttpClient 或 OkHttp 仍是更稳妥的选择

OkHttp 比 Apache HttpClient 更轻量,但拦截器链容易掩盖真实异常

OkHttp 的 OkHttpClient 初始化成本低、API 简洁,Call.execute() 同步调用不易阻塞线程,适合 Android 和微服务间调用。但它把网络层、重定向、Gzip 解压、Cookie 管理全封装进 Interceptor 链,出问题时堆栈里看不到原始 socket 异常。

排查典型问题的方法:

  • 启用日志拦截器前,先关掉所有自定义 Interceptor,确认基础请求是否通
  • 捕获 IOException 时,检查 e.getCause() —— 真实错误(如 DNS 失败、SSLHandshakeException)常藏在 cause 里
  • 使用 Response.peekBody(Long.MAX_VALUE) 查看原始响应体,避免因 response.body().string() 被消费两次导致空内容

多数场景下,OkHttp 是比 Apache HttpClient 更现代的选择,但别把它当黑盒——尤其在线上查超时或 502 错误时,得一层层拆开拦截器看。