GoGoYe

Back

c *gin.Context#

c *gin.Context是什么?#

你可以把一次来自浏览器、前端的HTTP请求想象成:

一个客户打电话到你的公司客服部,要办理一项业务。

这个场景里:

  • 「客户端」= 打电话的客户;
  • 「你的 Gin 处理函数」= 接电话的客服人员;
  • c *gin.Context​ = 客服手里的「客户沟通手册」+「业务办理工具包」。

这本 “手册 + 工具包” 里包含:

  1. 客户的所有信息: 手机号(请求 IP)、来电渠道(请求头)、要办的业务内容(请求参数 / 请求体);
  2. 客服能用到的所有工具: 给客户回复结果(返回 JSON / 字符串)、转接到其他部门(重定向)、拒绝办理(终止请求)等。

定义gin.Context是每一次独立的 HTTP 请求的「专属上下文对象」,一次请求对应一个 Context,请求结束后这个对象就会销毁。你要拿客户端的请求数据,必须通过它;你要给客户端返回响应,也必须通过它。

常用方法#

第一类:拿客户端的请求数据#

方法通俗解释示例代码
c.Query("参数名")拿 URL 里的 GET 参数(比如/user?name=张三​里的name),没传就返回空字符串
(附加信息,可选填)
name := c.Query("name") // 拿到"张三"
c.DefaultQuery("参数名", "默认值")同上,但如果客户端没传这个参数,就用你给的默认值age := c.DefaultQuery("age", "18") // 没传age就默认18
c.Param("参数名")拿 URL 路径里的动态参数(比如/user/:id​里的id​) (必填)// 路由:r.GET("/user/:id", handler)
id := c.Param("id") // 拿到路径里的id,比如/user/123 → "123"
c.PostForm("参数名")拿 POST 表单里的参数(比如前端表单提交的用户名 / 密码)username := c.PostForm("username")
c.ShouldBindJSON(&结构体)把客户端传的 JSON 请求体,直接解析到你定义的结构体里// 解析JSON到u里
//现在u.Name就是客户端传的name,u.Age就是传的age
if err := c.ShouldBindJSON(&u); err != nil {
c.JSON(400, gin.H{"msg": "参数错了"})
return
}
c.GetHeader("头名称")拿请求头里的信息(比如 Token、Content-Type)token := c.GetHeader("Authorization") // 拿登录令牌
FormFile(name string) (*multipart.FileHeader, error)处理用户上传的单个文件参数 name:前端表单中文件上传框的 name 属性值(比如前端<input type="file" name="avatar">,这里name就传"avatar");
返回值 1 *multipart.FileHeader:文件的 “头信息”,包含文件名、大小、临时存储路径等核心信息;
返回值 2 error:错误信息(比如前端没传文件、表单格式不对,都会返回非 nil 的错误)。

第二类:给客户端返回响应#

这是第二常用的场景,比如返回 JSON、普通字符串、网页等。

方法通俗解释示例代码
c.JSON(状态码, 数据)返回 JSON 格式的响应(前后端交互最常用),自动设置响应头为application/json// 200是HTTP状态码,gin.H等价于map[string]any
c.JSON(200, gin.H{ "code": 0, "msg": "操作成功", "data": "张三"})
c.String(状态码, 字符串)返回普通字符串响应(简单提示用)c.String(200, "欢迎访问我的接口!")
c.HTML(状态码, 模板名, 数据)返回 HTML 页面(比如渲染登录页、首页)// 假设你有templates/index.html模板,使用前需要先加载
r.LoadHTMLGlob("templates/*")
c.HTML(200, "index.html", gin.H{"title": "首页"})
c.File("文件路径")返回文件给客户端(比如下载图片、文档)c.File("./static/1.jpg") // 客户端会下载这张图片

第三类:其他高频操作#

方法通俗解释示例代码
c.Set("键", 值)在 Context 里存数据(比如中间件查了用户信息,存起来给后续处理函数用)// 中间件里存数据c.Set("user_level", "VIP")
c.Get("键")取上面Set存的数据(返回 “值 + 是否存在”)level, exists := c.Get("user_level")
if !exists { c.JSON(400, gin.H{"msg": "没找到用户等级"}) return}
c.Redirect(状态码, 目标URL)重定向客户端到另一个 URL(比如客户访问 /login,跳转到百度)c.Redirect(302, "https://www.baidu.com")
c.Abort()终止请求处理(比如客户没传 Token,直接拒绝,不执行后续逻辑)if token == "" { c.JSON(401, gin.H{"msg": "未登录"})
c.Abort() // 终止后续中间件/处理函数
return}

第四类:文件响应#

核心作用客户端表现
c.File("本地文件路径")直接返回文件浏览器能预览的文件(图片 / HTML / 视频)会直接展示,不能预览的(zip/Excel)会下载
c.FileAttachment("本地文件路径", "下载文件名")强制下载文件无论文件能否预览,都会触发 “下载” 操作,且文件名是你指定的(不是原文件名)
// 1. 本地文件的路径(这里用相对路径,也可以用绝对路径)
localFilePath := "./static/订单报表.xlsx"
// 2. 客户端下载时显示的文件名(可以和原文件名不同)
downloadFileName := "2026年1月订单报表.xlsx"
// 3. 强制下载文件(核心方法:FileAttachment) c.FileAttachment(localFilePath, downloadFileName)
c.Static("URL前缀", "本地文件夹路径")批量托管静态文件把整个文件夹的文件暴露给客户端,比如前端的 CSS/JS/ 图片,客户端可通过 URL 直接访问
r.Static("/", "./dist")​ 表示:所有以/​开头的 URL,都去./dist​文件夹找对应的文件(比如访问/css/app.css​ → 找./dist/css/app.css);

请求体格式#

(对应header中的Content-Type字段)

1. x-www-form-urlencoded 键值对#

name=jack&age=1234

出现场景:简单表单提交,拼在url中

2. form-data(multipart/form-data)分隔符#

分隔符(boundary) 做隔板,把不同参数(文本 / 图片 / Excel)分开

----------------------------853882779395683818968400  // 分隔符(boundary)
Content-Disposition: form-data; name="name"  // 说明这个格子装的是name参数

枫枫  // 参数值(空一行后是值)
----------------------------853882779395683818968400  // 下一个隔板
Content-Disposition: form-data; name="age"

1234
----------------------------853882779395683818968400--  // 结束符(--结尾)
plaintext

3. JSON格式#

{
    "name": "枫枫",  // 键用双引号,值是字符串
    "age": 23       // 值可以是数字(不用引号)
}
json

绑定方法#

方法绑定来源核心特点适用场景
ShouldBindQuery仅 URL 查询参数(? 后)只查查询参数,优先级高GET 请求传参
ShouldBindUri仅 URL 路径动态参数(/:xxx)只查路径参数,必须路由匹配路径传动态标识(比如 /user/:id)
ShouldBind自动识别 Content-Type智能匹配(表单 / JSON / 查询参数)POST/PUT 请求传复杂参数
ShouldBindJSON仅 JSON 请求体只解析 JSON,效率更高前后端分离的 JSON 接口

绑定规则#

一、基础规则#

1. required:必填字段#

  • 含义​:参数必须传,且不能是空值(比如空字符串""、0 不算空,但空数组算);
  • 通俗理解:收快递必须填收件人姓名,没填就拒收;
  • 示例
type User struct {
    Name string `form:"name" binding:"required"` // 必须传name,且不能是空字符串
    Age  int    `form:"age" binding:"required"` // 必须传age(0也可以,int的空值是0)
}
go
  • 校验失败场景​:客户端没传name​参数,或传name=(空字符串)。

2. -:忽略字段#

  • 含义:Gin 既不绑定这个字段,也不做任何校验;
  • 通俗理解:这个字段不需要验收,直接跳过;
  • 示例
type User struct {
    ID   string `form:"id" binding:"-"` // 完全忽略,不绑定也不校验
    Note string `form:"note"`           // 不写binding标签,等价于binding:"-"(只绑定,不校验)
}
go

二、字符串规则#

1. 长度规则:min=5 /max=10 /len=6#

  • 含义

    • min=5:字符串长度≥5;
    • max=10:字符串长度≤10;
    • len=6:字符串长度必须等于 6(比如验证码);
  • 示例

type User struct {
    Nickname string `form:"nickname" binding:"required,min=2,max=10"` // 昵称2-10个字符
    Code     string `form:"code" binding:"required,len=6"`           // 验证码必须6位
}
go

2. 内容匹配规则:contains /excludes/startswith /endswith#

  • 含义

    • contains=fengfeng:字符串必须包含 “fengfeng”;
    • excludes=test:字符串不能包含 “test”;
    • startswith=138:字符串前缀是 138(比如手机号);
    • endswith=8888:字符串后缀是 8888;
  • 示例

type User struct {
    Phone  string `form:"phone" binding:"required,startswith=138,endswith=8888"` // 138xxxx8888
    Remark string `form:"remark" binding:"contains=fengfeng,excludes=垃圾"`         // 包含fengfeng,不含“垃圾”
}
go

三、数字规则(int/float 等数值类型)#

含义(对比关系):#

  • eq=3:等于 3;
  • ne=12:不等于 12;
  • gt=10​:大于 10(>);
  • gte=10:大于等于 10(≥);
  • lt=10​:小于 10(<);
  • lte=10:小于等于 10(≤);

示例(商品价格 / 库存校验):#

type Goods struct {
    Price int `form:"price" binding:"required,gt=0,lte=9999"` // 价格>0且≤9999
    Stock int `form:"stock" binding:"eq=0"`                   // 库存必须为0(售罄标识)
    Type  int `form:"type" binding:"ne=3"`                    // 类型不能是3
}
go

四、字段对比规则(和同级字段比)#

1. eqfield = 字段名:等于另一个字段的值#

  • 核心场景:确认密码(两次输入的密码必须一致);

2. nefield = 字段名:不等于另一个字段的值#

  • 核心场景:新密码不能和旧密码相同;

示例(注册 / 改密码):#

type Register struct {
    Password        string `form:"password" binding:"required,min=6"` // 新密码≥6位
    ConfirmPassword string `form:"confirm_pwd" binding:"required,eqfield=Password"` // 和密码一致
    OldPassword     string `form:"old_pwd" binding:"nefield=Password"` // 不能和新密码相同
}
go

五、枚举规则:oneof = 值 1 值 2#

  • 含义​:参数值只能是指定的几个值之一(注意:值之间用空格分隔,值本身不能有空格);
  • 示例
type Color struct {
    Name string `form:"name" binding:"required,oneof=red green blue"` // 只能是这三个值
}
go
  • 校验失败场景​:传name=yellow(不在枚举列表里)。

六、数组专属规则:dive#

  • 含义​:dive​是 “穿透” 的意思,后面的规则会作用于​数组的每一个元素
  • 示例
type ScoreList struct {
    // 数组必填,每个元素必须>0且<100(比如考试分数)
    Scores []int `form:"scores" binding:"required,dive,gt=0,lt=100"`
    // 每个标签长度≥2(比如["java","go"]合法,["c"]不合法)
    Tags   []string `form:"tags" binding:"dive,min=2"`
}
go
  • 关键​:dive必须写在数组规则的前面,后面跟元素的校验规则。

七、网络格式规则(字符串类型)#

含义(区分 URI 和 URL):#

  • ip:合法 IP(IPv4/IPv6 都可以);
  • ipv4:仅合法 IPv4(比如 192.168.1.1);
  • ipv6:仅合法 IPv6(比如 2001:0db8:85a3:0000:0000:8a2e:0370:7334);
  • uri​:统一资源标识符(比如/user/1001,只标识资源,不指定访问方式);
  • url​:统一资源定位符(比如https://www.baidu.com,包含访问方式 + 路径);

示例:#

type NetInfo struct {
    IP  string `form:"ip" binding:"required,ipv4"` // 必须是IPv4地址
    URL string `form:"url" binding:"url"`          // 必须是合法URL(比如https://xxx)
    URI string `form:"uri" binding:"uri"`          // 必须是合法URI(比如/api/user)
}
go

八、日期格式规则:datetime = 格式#

  • 示例
type Order struct {
    // 必须是YYYY-MM-DD格式(比如2026-01-21)
    CreateDate string `form:"create_date" binding:"required,datetime=2006-01-02"`
    // 必须是YYYY-MM-DD HH:mm:ss格式(比如2026-01-21 14:30:00)
    CreateTime string `form:"create_time" binding:"datetime=2006-01-02 15:04:05"`
}
go

中间件执行流程#

c.Next()#

访问http://127.0.0.1:8080/api/users,打印结果:#

GM1 请求部分(前处理)
GM2 请求部分(前处理)
Home 处理函数执行
GM2 响应部分(后处理)
GM1 响应部分(后处理)
plaintext

c.Abort()#

func GM1(c *gin.Context) {
  fmt.Println("GM1 请求部分(前处理)")
  c.Abort() // 终止后续所有逻辑
  fmt.Println("GM1 响应部分(后处理)")
}
go

访问/api/users,打印结果:#

GM1 请求部分(前处理)
GM1 响应部分(后处理)
plaintext

GM2 和 Home 都没有执行(流水线被终止);

gin.Context
https://gogo-ye.github.io/blog/2gincontext/gincontext
Author GoGoYe
Published at 2026/01/22