1. 概述

本文将介绍在Java中执行HTTP请求的基础方法——使用内置的 HttpUrlConnection 类。

⚠️ 注意:从JDK 11开始,Java提供了新的 HttpClient API 作为 HttpUrlConnection 的替代方案。但本文聚焦于经典的 HttpUrlConnection 实现。

2. HttpUrlConnection 详解

HttpUrlConnection 类允许我们在不依赖任何第三方库的情况下执行基础HTTP请求。所有必需的类都位于 java.net 包中。

✅ 优点:

  • 零外部依赖
  • JDK原生支持

❌ 缺点:

  • 代码比现代HTTP库更繁琐
  • 缺少高级功能(如专门的Header添加或认证方法)

3. 创建请求

通过 URL 类的 openConnection() 方法创建 HttpUrlConnection 实例。注意:此方法仅创建连接对象,尚未建立实际连接。

通过设置 requestMethod 属性指定请求类型(GET/POST/HEAD/OPTIONS/PUT/DELETE/TRACE):

URL url = new URL("http://example.com");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");

4. 添加请求参数

若需添加参数,**必须将 doOutput 设为 true,然后将 param1=value&param2=value 格式的字符串写入 OutputStream**:

Map<String, String> parameters = new HashMap<>();
parameters.put("param1", "val");

con.setDoOutput(true);
DataOutputStream out = new DataOutputStream(con.getOutputStream());
out.writeBytes(ParameterStringBuilder.getParamsString(parameters));
out.flush();
out.close();

为方便参数转换,我们创建了工具类 ParameterStringBuilder,其静态方法 getParamsString()Map 转换为所需格式:

public class ParameterStringBuilder {
    public static String getParamsString(Map<String, String> params) 
      throws UnsupportedEncodingException{
        StringBuilder result = new StringBuilder();

        for (Map.Entry<String, String> entry : params.entrySet()) {
          result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
          result.append("=");
          result.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
          result.append("&");
        }

        String resultString = result.toString();
        return resultString.length() > 0
          ? resultString.substring(0, resultString.length() - 1)
          : resultString;
    }
}

5. 设置请求头

通过 setRequestProperty() 方法添加请求头:

con.setRequestProperty("Content-Type", "application/json");

读取响应头使用 getHeaderField()

String contentType = con.getHeaderField("Content-Type");

6. 配置超时

HttpUrlConnection 支持设置连接超时和读取超时。这些值定义了等待服务器连接建立或数据可读的时间间隔。

使用 setConnectTimeout()setReadTimeout() 配置:

con.setConnectTimeout(5000);  // 5秒连接超时
con.setReadTimeout(5000);    // 5秒读取超时

7. 处理Cookie

java.net 包提供了 CookieManagerHttpCookie 等类简化Cookie操作。

读取响应中的Cookie

String cookiesHeader = con.getHeaderField("Set-Cookie");
List<HttpCookie> cookies = HttpCookie.parse(cookiesHeader);

将Cookie添加到Cookie存储

cookies.forEach(cookie -> cookieManager.getCookieStore().add(null, cookie));

检查是否存在名为 username 的Cookie,若不存在则添加值为 john

Optional<HttpCookie> usernameCookie = cookies.stream()
  .findAny().filter(cookie -> cookie.getName().equals("username"));
if (usernameCookie == null) {
    cookieManager.getCookieStore().add(null, new HttpCookie("username", "john"));
}

在请求中添加Cookie(需先断开并重建连接):

con.disconnect();
con = (HttpURLConnection) url.openConnection();

con.setRequestProperty("Cookie", 
  StringUtils.join(cookieManager.getCookieStore().getCookies(), ";"));

8. 处理重定向

通过 setInstanceFollowRedirects() 控制是否自动跟随重定向

con.setInstanceFollowRedirects(false);  // 禁用自动重定向

全局控制重定向行为

HttpUrlConnection.setFollowRedirects(false);  // 所有连接禁用

默认启用自动重定向。当收到301/302状态码时,可手动处理:

if (status == HttpURLConnection.HTTP_MOVED_TEMP
  || status == HttpURLConnection.HTTP_MOVED_PERM) {
    String location = con.getHeaderField("Location");
    URL newUrl = new URL(location);
    con = (HttpURLConnection) newUrl.openConnection();
}

9. 读取响应

通过解析 HttpUrlConnectionInputStream 读取响应内容

执行请求(调用以下任一方法):

  • getResponseCode()
  • connect()
  • getInputStream()
  • getOutputStream()
int status = con.getResponseCode();

读取响应内容到字符串:

BufferedReader in = new BufferedReader(
  new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer content = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
    content.append(inputLine);
}
in.close();

关闭连接

con.disconnect();

10. 处理失败请求的响应

请求失败时,直接读取 getInputStream() 会抛异常。**应改用 getErrorStream()**:

根据HTTP状态码选择流:

int status = con.getResponseCode();

Reader streamReader = null;

if (status > 299) {
    streamReader = new InputStreamReader(con.getErrorStream());
} else {
    streamReader = new InputStreamReader(con.getInputStream());
}

后续读取方式与第9节相同。

11. 构建完整响应

HttpUrlConnection 无法直接获取完整响应,但可组合其提供的方法构建:

public class FullResponseBuilder {
    public static String getFullResponse(HttpURLConnection con) throws IOException {
        StringBuilder fullResponseBuilder = new StringBuilder();

        // 读取状态和消息
        fullResponseBuilder.append(con.getResponseCode())
          .append(" ")
          .append(con.getResponseMessage())
          .append("\n");

        // 读取头部
        con.getHeaderFields().entrySet().stream()
          .filter(entry -> entry.getKey() != null)
          .forEach(entry -> {
              fullResponseBuilder.append(entry.getKey()).append(": ");
              List headerValues = entry.getValue();
              Iterator it = headerValues.iterator();
              if (it.hasNext()) {
                  fullResponseBuilder.append(it.next());
                  while (it.hasNext()) {
                      fullResponseBuilder.append(", ").append(it.next());
                  }
              }
              fullResponseBuilder.append("\n");
        });

        // 读取响应内容(根据状态选择流)
        // ...(此处省略流读取代码,逻辑同第10节)

        return fullResponseBuilder.toString();
    }
}

12. 总结

本文展示了使用 HttpUrlConnection 执行HTTP请求的核心方法。虽然代码略显繁琐,但作为JDK原生方案,在无第三方依赖的场景下仍是可靠选择。

完整示例代码见 GitHub仓库


原始标题:Do a Simple HTTP Request in Java | Baeldung