go中设置请求头必须用req.header.set()或add()手动添加,需确保req由http.newrequest创建且未被do()调用;单值头如authorization用set(),多值头如cookie用add(),host等受管字段不可设。

Go 里没有“设置请求头的函数”这种现成工具,http.NewRequest 返回的 *http.Request 对象本身不带默认 Header,你必须手动调用 req.Header.Set() 或 req.Header.Add() —— 不是封装成函数就完事,而是时机、对象、字段三者都得对,否则白写。
为什么自己写的 setHeader 函数总失效
常见错误是把函数写成“给任意 req 加头”,但没检查 req 是否为 nil、是否已被 Do() 调用过、是否来自 http.Get() 等快捷函数的只读副本。
-
req必须由http.NewRequest()或http.NewRequestWithContext()创建,且err已处理;否则req.Header.Set()会 panic - 函数里若调用了
client.Do(req),那后续再设 Header 就无效 —— 冻结了 - 别传入
resp.Request.Header(比如从http.Get()拿来的),它是只读快照,改了也不发出去 - 调试时加一句
fmt.Printf("header: %+v\n", req.Header),比反复改函数逻辑快得多
req.Header.Set() 和 req.Header.Add() 该怎么封装
不能无脑统一用 Set(),也不能全用 Add() —— 它们语义不同,封装前得先分清字段类型。
- 单值字段(
Authorization、Content-Type、User-Agent)一律用Set():避免残留旧 token 或错乱的 MIME 类型 - 多值字段(
Cookie、Accept、X-Forwarded-For)必须用Add():否则第二次调用会覆盖第一次 - 别封装成
setHeader(req, "Cookie", "a=1")这种通用接口 —— 容易误用。更安全的是按场景拆:比如setAuthHeader(req, token)内部用Set(),addCookie(req, name, value)内部用Add()
Host 和 Content-Length 为什么不能进封装函数
这两个字段 Go 自动管理,硬塞进函数里不仅无效,还可能引发错误。
-
Host不能通过req.Header.Set("Host", ...)设置,得用req.Host = "example.com"或确保url参数含完整 host(如"https://api.example.com/path") -
Content-Length由 body 的io.Reader实现自动推算(如strings.NewReader、bytes.NewReader可返回长度);手动设错会触发http: invalid Content-Length - 其他被保护字段:
Connection、Transfer-Encoding、Trailer同理,封装函数里遇到就直接跳过或 panic 提示
自定义 client 时 Header 复用的坑
很多人想写个 “带默认 Header 的 client”,结果发现 Header 没生效 —— 因为 http.Client 本身没有 Header 字段,Header 属于每个 *http.Request 实例。
- 别写
client.Header.Set(...)—— 编译不过,http.Client没这个字段 - 想复用逻辑?封装一个工厂函数,比如
newRequestWithDefaultHeaders(method, url, body),里面统一调req.Header.Set() - 如果用 context 控制生命周期,确保
req.Header设置发生在http.NewRequestWithContext()返回后、client.Do()前 - 注意
req.Clone()不复制Body,重试时若需保留 body,得手动重新赋值req.Body
最麻烦的不是怎么写函数,而是每次调用前都要确认:这个 req 是不是新建的、有没有被冻结、body 类型和 Content-Type 匹配不匹配 —— 这些细节不盯住,函数写得再漂亮也发不出正确请求。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











