httpurlconnection 默认不发送 post 请求体,需显式调用 setdooutput(true)、getoutputstream() 写入数据并 flush/close;302 重定向不携带请求体;超时需同时设 connecttimeout 和 readtimeout;字符编码必须手动指定,不可依赖默认。

HttpURLConnection 默认不发送 POST 请求体
很多人调用 setRequestMethod("POST") 后发现服务端收不到参数,其实 HttpURLConnection 默认不会自动写入请求体——哪怕你设置了 setDoOutput(true),也得手动调用 getOutputStream() 并写入数据,否则请求体为空。
常见错误现象:400 Bad Request 或后端解析出空参数;使用抓包工具(如 Wireshark、Charles)能看到请求头有 Content-Length: 0。
- 必须显式调用
conn.setDoOutput(true)(仅设POST不够) - 写入前检查
conn.getOutputStream()是否可写(某些代理或重定向后会失效) - 写入后记得
flush()和close(),否则部分 JDK 版本可能卡住 - 如果传 JSON,别忘了设置
conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8")
响应码 302 重定向默认不自动跟随
HttpURLConnection 默认开启重定向(setInstanceFollowRedirects(true)),但只对 GET 和 HEAD 生效;遇到 POST 返回 302 时,它会直接返回 302,**不会**把原请求体带上新地址重发——这和浏览器或 OkHttp 行为不同。
后果是:你以为提交成功了,实际卡在跳转页,服务端没收到数据。
立即学习“Java免费学习笔记(深入)”;
- 手动处理重定向更可靠:检查
getResponseCode() == 302,读取getHeaderField("Location"),再新建连接发起请求 - 若必须自动跳转,改用
POST→GET场景(如登录后跳首页),可接受丢弃请求体 - JDK 11+ 的
HttpClient支持自定义重定向策略,HttpURLConnection没这个能力
超时设置容易被忽略的三处地方
只设 setConnectTimeout() 不代表整个请求受控——网络连上之后卡住、DNS 慢、服务端迟迟不发响应体,这些都归 setReadTimeout() 管;而 setInstanceFollowRedirects() 开启后,重定向链路里的每次请求也各自独立计时。
典型表现:程序卡死几十秒才抛 SocketTimeoutException,但你以为只设了连接超时。
-
setConnectTimeout(5000):TCP 握手 + TLS 握手上限 -
setReadTimeout(10000):从调用getInputStream()开始,到读完首字节/持续无数据的时间 - 如果手动处理重定向,每次新建
HttpURLConnection都要重新设超时 - 注意:超时单位是毫秒,不是秒;设
0表示无限等待
字符编码不一致导致中文乱码
服务端返回 UTF-8 编码的 JSON,但你用 new String(bytes) 直接转字符串,结果出现 ;或者 POST 表单含中文,服务端收到乱码。根本原因是没显式指定字符集。
HttpURLConnection 不会自动从 Content-Type 解析 charset 并用于响应体解码——它只影响 getContentType() 返回值,不参与实际读取逻辑。
- 读响应体时,先用
conn.getContentType()提取charset=xxx,再用InputStreamReader(in, charset) - POST 表单建议用
URLEncoder.encode(value, "UTF-8"),并设请求头Content-Type: application/x-www-form-urlencoded; charset=UTF-8 - 避免依赖平台默认编码(如 Windows 上是 GBK),所有
String↔byte[]转换必须带明确 charset 参数
HttpURLConnection 表面简单,但每个环节都藏着隐式约定和 JDK 版本差异。最常踩的坑不是“不会用”,而是“以为它像浏览器或高级库一样智能”。真要稳定用,就得亲手控制输出流、手动处理跳转、逐字节确认编码——它本质是个裸 TCP 连接封装器,不是 HTTP 客户端。










