Golang HTTP请求Header自定义处理
在 Web 开发中,HTTP 头部承载着请求与响应的元数据,如内容类型、认证信息、缓存控制等。Golang 的标准库 net/http 提供了简洁而强大的接口来操作这些头部信息。无论是作为客户端发送请求,还是作为服务端处理请求,我们都需要灵活地读写 Header。本文将详细介绍在 Go 中如何处理 HTTP 请求头与响应头的各种场景。
HTTP Header 基础
HTTP 头部是一系列键值对,键为字符串,值可以是单个字符串或字符串切片。Go 中使用 http.Header 类型表示,其本质是 map[string][]string。规范规定头部名称不区分大小写,但访问时通常会使用标准格式(首字母大写)。
客户端:设置请求头
当我们通过 http.Client 发送请求时,需要构造 http.Request 对象并设置头部。Request 的 Header 字段是 http.Header 类型,提供了 Set、Add、Get 等方法。
示例:GET 请求自定义 Header
以下代码创建一个 GET 请求,并设置了 User-Agent 和 X-Custom-Header。
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
client := &http.Client{}
// 创建请求
req, err := http.NewRequest("GET", "http://ipipp.com/api/data", nil)
if err != nil {
fmt.Println("创建请求失败:", err)
return
}
// 设置请求头
req.Header.Set("User-Agent", "MyCustomClient/1.0")
req.Header.Add("X-Custom-Header", "my-value")
// 注意:Set 会覆盖已有的同名值,Add 则追加
resp, err := client.Do(req)
if err != nil {
fmt.Println("请求失败:", err)
return
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println("响应状态:", resp.Status)
fmt.Println("响应体:", string(body))
}示例:POST 请求设置 Content-Type
发送 JSON 数据时,需要同时设置 Content-Type 头部。
package main
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
)
func main() {
jsonData := []byte(`{"name":"golang","type":"tutorial"}`)
req, err := http.NewRequest("POST", "http://ipipp.com/api/submit", bytes.NewBuffer(jsonData))
if err != nil {
fmt.Println("创建请求失败:", err)
return
}
// 设置 Content-Type
req.Header.Set("Content-Type", "application/json")
// 也可以添加认证令牌
req.Header.Set("Authorization", "Bearer your-token-here")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("请求失败:", err)
return
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println("响应:", string(body))
}服务端:读取请求头
在服务端,通过 http.Request 的 Header 字段可以读取客户端发送的所有头部信息。使用 Header.Get(key) 可以获取第一个值,使用 Header.Values(key) 获取所有值。
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
// 读取特定头部,如果不存在返回空字符串
userAgent := r.Header.Get("User-Agent")
contentType := r.Header.Get("Content-Type")
// 读取自定义头部
custom := r.Header.Get("X-Custom-Header")
// 获取所有同名头部的值
acceptEncodings := r.Header.Values("Accept-Encoding")
fmt.Println("User-Agent:", userAgent)
fmt.Println("Content-Type:", contentType)
fmt.Println("Custom:", custom)
fmt.Println("Accept-Encoding values:", acceptEncodings)
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}服务端:设置响应头
响应头通过 http.ResponseWriter 的 Header() 方法获取 http.Header 对象,然后进行设置。注意:头部设置必须在写入状态码 (WriteHeader) 或写入响应体 (Write) 之前完成,否则修改无效。
func apiHandler(w http.ResponseWriter, r *http.Request) {
// 设置响应头
w.Header().Set("Content-Type", "application/json")
w.Header().Set("X-Custom-Response", "hello-from-server")
// 状态码未设置时,Write 会自动调用 WriteHeader(http.StatusOK)
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"message": "success"}`))
}中间件:统一处理 Header
在实际项目中,我们常通过中间件来统一处理公共头部,如 CORS、安全策略等。下面是一个添加跨域头的中间件示例。
package main
import (
"net/http"
)
// CORS中间件:为所有响应添加允许跨域的头部
func CORSMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 设置 CORS 头
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
// 处理预检请求
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusOK)
return
}
// 调用下一个处理器
next.ServeHTTP(w, r)
})
}
func mainHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, CORS!"))
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", mainHandler)
// 使用中间件包装 handler
handler := CORSMiddleware(mux)
http.ListenAndServe(":8080", handler)
}常见头部操作技巧
删除头部:可以使用
Header.Del(key)方法删除指定的头部。遍历头部:通过
for key, values := range r.Header遍历所有头部信息。大小写注意:虽然 Header 访问不区分大小写,但设置时建议使用规范格式(如
Content-Type而不是content-type),以避免中间代理的意外处理。自定义头部:推荐使用
X-前缀来命名非标准头部,但现代 HTTP 规范已不强制要求。应避免与标准头部冲突。
总结
Golang 的 net/http 包提供了便捷直观的 Header 操作接口,无论是在客户端构造请求还是服务端解析、返回响应,都能通过简单的 API 完成。掌握这些基本技巧,可以帮助我们更好地控制 HTTP 通信,实现用户认证、内容协商、跨域控制等关键功能。建议在实际开发中合理使用中间件来统一管理公共头部,保持代码的整洁与可维护性。