第07章:请求头与客户端信息
本章目标
学完本章后,你将能够:
- 使用
c.GetHeader()读取请求头 - 使用
c.ClientIP()获取客户端真实 IP - 辨别常用请求头的作用
- 正确设置响应头
前置知识
- 需要先学习:第05-06章(参数读取)
7.1 读取请求头
go
r.GET("/headers", func(c *gin.Context) {
// 方式一:c.GetHeader()(推荐)
auth := c.GetHeader("Authorization")
contentType := c.GetHeader("Content-Type")
// 方式二:c.Request.Header(标准库方式)
userAgent := c.Request.Header.Get("User-Agent")
// 遍历所有请求头
fmt.Println("=== 所有请求头 ===")
for key, values := range c.Request.Header {
for _, v := range values {
fmt.Printf("%s: %s\n", key, v)
}
}
c.JSON(200, gin.H{
"authorization": auth,
"content_type": contentType,
"user_agent": userAgent,
})
})7.2 常用请求头
| 请求头 | 含义 | 用途 |
|---|---|---|
Authorization | 认证令牌 | Bearer eyJhbG... |
Content-Type | Body 数据类型 | application/json |
Accept | 客户端接受的响应格式 | application/json |
User-Agent | 客户端标识 | Mozilla/5.0 ... |
Cookie | 会话 Cookie | session_id=abc123 |
X-Request-ID | 请求追踪 ID | 日志关联 |
X-Forwarded-For | 代理传递的真实 IP | 1.2.3.4 |
Referer | 来源页面 | https://example.com |
7.3 获取客户端 IP
go
r.GET("/ip", func(c *gin.Context) {
// 推荐:自动处理代理头
clientIP := c.ClientIP()
// 原始方式(不推荐,拿不到代理后的真实IP)
remoteAddr := c.Request.RemoteAddr
c.JSON(200, gin.H{
"client_ip": clientIP,
"remote_addr": remoteAddr,
})
})📌
c.ClientIP()会智能判断:先检查X-Forwarded-For和X-Real-Ip头(反向代理设置),没有才用RemoteAddr。
让 Gin 信任代理
go
// 如果服务在 Nginx 后面,设置信任代理
r.SetTrustedProxies([]string{"127.0.0.1", "10.0.0.0/8"})7.4 设置响应头
go
r.GET("/download", func(c *gin.Context) {
// 设置单个响应头
c.Header("Content-Disposition", "attachment; filename=report.pdf")
c.Header("X-Custom-Header", "hello-world")
// 多次设置同一个头会覆盖
c.Header("X-Custom-Header", "new-value")
c.File("./report.pdf")
})
// 预检请求处理
r.OPTIONS("/api/data", func(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization")
c.Status(http.StatusNoContent)
})7.5 Content-Type 判断
go
r.POST("/api", func(c *gin.Context) {
// 方式一:c.ContentType()
ct := c.ContentType()
// 方式二:c.GetHeader()
ct2 := c.GetHeader("Content-Type")
// 判断是否是 JSON
if ct == "application/json" {
// 用 ShouldBindJSON
} else if ct == "application/x-www-form-urlencoded" {
// 用 PostForm
}
c.JSON(200, gin.H{
"detected_type": ct,
"raw_header": ct2,
})
})常见错误
错误1:混淆 Request Header 和 Response Header
go
// ❌ 在请求处理中读取响应头
r.GET("/test", func(c *gin.Context) {
v := c.GetHeader("X-Custom") // 读取的是"请求"头
// 即使刚设置过响应头,GetHeader 也只读请求头
})
// ✅ 设置响应头用 c.Header()
r.GET("/test", func(c *gin.Context) {
c.Header("X-Custom", "value") // 设置响应头
})错误2:拿反向代理后的 IP 用 RemoteAddr
go
// ❌ 如果用了 Nginx,RemoteAddr 是 Nginx 的地址(127.0.0.1)
ip := c.Request.RemoteAddr // 永远是 127.0.0.1
// ✅ 用 ClientIP(),自动处理 X-Forwarded-For
ip := c.ClientIP() // 真实客户端 IP错误3:大小写问题
go
// HTTP 头是大小写不敏感的,以下写法都可以:
c.GetHeader("Authorization")
c.GetHeader("authorization")
c.GetHeader("AUTHORIZATION")
// 但 Go 的 http.Header.Get() 会自动做规范化(首字母大写)
// 所以推荐用标准写法:Authorization本章小结
c.GetHeader("name")读取请求头c.Header("name", "value")设置响应头c.ClientIP()获取真实客户端 IP(自动处理代理)c.ContentType()判断请求的 Content-Type- 常用头:Authorization(认证)、Content-Type(格式)、User-Agent(标识)
练习题
- 写一个
/headers接口,返回所有请求头的 JSON。 - 写一个
/ip接口,分别返回ClientIP()和RemoteAddr的值。 - 写一个接口,根据请求的 Content-Type 返回不同的响应(JSON 请求返回 "json received",表单请求返回 "form received")。
- (思考题)为什么
c.ClientIP()比c.Request.RemoteAddr更准确?Nginx 反向代理时发生了什么?