在Java中HttpURLConnection如何发送请求_Java基础网络通信解析

HttpURLConnection 默认是 GET,但需显式调用 setRequestMethod("GET") 或不调用该方法;调用 getOutputStream() 会自动切为 POST 并抛 IllegalStateException。

HttpURLConnection 默认是 GET 还是 POST?

默认是 GET,但必须显式调用 setRequestMethod("GET") 或不调用 setRequestMethod —— 否则首次 connect()getOutputStream() 时会隐式设为 GET。一旦尝试写入请求体(比如调用 getOutputStream()),它会自动切换成 POST,哪怕你之前设过 GET,这时会抛 IllegalStateException

常见错误现象:

java.lang.IllegalStateException: Already connected
java.lang.IllegalStateException: POST body missing
,往往是因为先写了输出流、又去读响应,却忘了设置请求方法或 Content-Length。

  • 想发 GET:不调用 setRequestMethod,也不调用 getOutputStream()
  • 想发 POST:必须调用 setRequestMethod("POST"),且在 connect() 前设置 setDoOutput(true)
  • setDoInput(true) 默认为 true,读响应不需要额外设置;但 setDoOutput(false) 后再设 true 会报错

如何正确设置请求头与超时?

setRequestProperty 只能在未连接(connect() 前)调用,否则抛 IllegalStateException。超时值单位是毫秒,且三个超时需分别设置:

  • setConnectTimeout(5000):建立 TCP 连接的上限时间
  • setReadTimeout(10000):从 socket 读取响应数据的单次阻塞上限(不是整个响应)
  • setInstanceFollowRedirects(false):禁用自动重定向,否则 302/307 响应会被内部消化,你拿不到原始响应码和 header

常用请求头示例:

conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
conn.setRequestProperty("Accept", "application/json");
conn.setRequestProperty("User-Agent", "Java-http-client/1.0");

为什么 getInputStream() 有时抛 IOException?

根本原因:HTTP 状态码 ≥ 400 时,getInputStream() 不可用,必须改用 getErrorStream()。直接调用会抛 IOException: Server returned HTTP response code: 400 这类异常。

正确做法是先查状态码:

int responseCode = conn.getResponseCode();
if (responseCode >= 400) {
    is = conn.getErrorStream(); // 而不是 getInputStream()
} else {
    is = conn.getInputStream();
}

  • 别依赖 getResponseMessage() 判断成功与否,它可能返回 null 或空字符串
  • 即使状态码是 200,也要检查 Content-Length

    或用 read 循环直到返回 -1,避免流提前关闭
  • 务必在 finally 块中关闭 InputStreamOutputStream,否则连接可能复用失败或泄漏

POST 发送 JSON 数据要注意什么?

关键点不在“怎么转 JSON”,而在“怎么让服务端正确解析”。HttpURLConnection 不会自动加 Content-Length,如果用 writeBytes 写字符串,得自己算字节长度并调用 setFixedLengthStreamingMode,否则某些服务器(如 Spring Boot 默认配置)会拒绝请求。

推荐方式(避免 chunked 编码):

String json = "{\"name\":\"Alice\",\"age\":30}";
byte[] jsonBytes = json.getBytes(StandardCharsets.UTF_8);
conn.setFixedLengthStreamingMode(jsonBytes.length);
conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
conn.setDoOutput(true);

try (OutputStream os = conn.getOutputStream()) {
    os.write(jsonBytes);
}

  • 不要用 writeBytes(str),它按平台默认编码写,不可靠;必须用 getBytes(UTF_8) 显式编码
  • setChunkedStreamingMode(0) 会启用分块传输,部分老旧网关或代理不支持
  • 如果服务端要求带 Bearer Token,header 必须写成 Authorization: Bearer xxx,漏掉 Bearer 前缀会导致 401

底层没有连接池,每次 new URL().openConnection() 都是新连接;高频调用务必自己缓存 HttpURLConnection 实例或迁移到 OkHttp。