header() 追加同名请求头,setheader() 覆盖同名请求头;java 17+ 新增 setheader(),java 11 不支持;body 字符串需显式指定 utf-8 编码,content-type 必须与 bodypublisher 类型匹配;cookie 需手动设置或配合 cookiehandler 管理;builder 不可变,每次调用返回新实例,build() 仅能调用一次。

HttpRequest.Builder 设置自定义请求头时,header() 和 setHeader() 有啥区别?
header() 是追加模式,同名头可以多次调用,最终会以多个相同字段出现在请求中(HTTP/1.1 允许);setHeader() 是覆盖模式,后调用的会完全替换前一次对同一名称的设置。实际开发中容易误用 header() 导致重复头,比如两次 header("Authorization", "Bearer a"),服务端可能只取第一个或直接拒收。
- 如果需要确保唯一性(如
Content-Type、Accept),优先用setHeader() - 如果目标服务明确要求多值头(如
Cookie或自定义追踪头X-Request-ID),才用header() - 注意:Java 11 的
HttpRequest.Builder没有setHeader()—— 这是 Java 17+ 新增方法,低版本只能靠先build()再手动构造,或用第三方库兜底
构建带认证和 JSON Body 的 POST 请求,Body 和 Header 怎么配才不翻车?
HttpRequest.newBuilder() 要求 BodyPublisher 必须与 Content-Type 头一致,否则多数 REST 服务会返回 415 Unsupported Media Type。常见错误是写了 setHeader("Content-Type", "application/json"),但 Body 用的是 HttpRequest.BodyPublishers.ofString("...") 而没做 UTF-8 编码声明,导致中文乱码或解析失败。
- Body 字符串必须显式指定编码:
HttpRequest.BodyPublishers.ofString(json, StandardCharsets.UTF_8) - 不要手动拼接 JSON 字符串,避免引号/转义出错;建议用 Jackson/Gson 序列化后传入
-
Content-Length头由 JDK 自动计算并添加,别自己设,否则可能冲突导致连接被重置
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/v1/users"))
.header("Authorization", "Bearer xyz")
.setHeader("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString("{\"name\":\"张三\"}", StandardCharsets.UTF_8))
.build();
用 HttpRequest.Builder 发送含 Cookie 的请求,为什么服务端收不到?
JDK 的 HttpClient 默认不自动管理 Cookie,HttpRequest.Builder 更不会读取或写入 Cookie 头——它只是个构造器,不维护状态。你写的 header("Cookie", "sid=abc") 会被发出去,但后续重定向或重试时不会自动带上,也不会从响应里提取新 Cookie 并回填。
- 若需完整 Cookie 管理,必须配合
HttpClient.newBuilder().cookieHandler(...)使用CookieManager - 单次请求硬编码
Cookie头没问题,但要注意有效期和作用域(Path、Domain) - 常见坑:把
Set-Cookie响应头里的值原样当请求头用,漏掉Expires/Max-Age判断,结果发了个已过期的 Cookie
流式构建时链式调用中断,Builder 状态会不会残留?
不会。HttpRequest.Builder 是不可变对象,每次调用 header()、GET()、POST(...) 都返回一个新 Builder 实例,原始实例不受影响。但这也意味着:如果中间某步抛异常(比如 URI 解析失败),前面设好的头、method、body 都白写了,没法“回滚”。
立即学习“Java免费学习笔记(深入)”;
- 别在链式调用中途插逻辑判断,容易断链;建议分步构建,用变量暂存 Builder
- 所有 setter 类方法(
timeout()、version())都遵循同样不可变规则,没有副作用 - 一个易忽略点:
build()只能调用一次,重复调用会抛IllegalStateException,不是因为状态残留,而是 Builder 内部标记了“已构建”
事情说清了就结束










