第24讲 Gin设置controller包
本讲基于视频实际讲解内容整理
一、本讲概述
在前几讲中,我们的 Gin 路由和处理函数都写在 main.go 里。随着功能增多,这种做法会让 main.go 变得越来越臃肿、难以维护。本讲讲解如何把处理函数(Handler)抽离到独立的 controller(控制器)包 中,让项目结构更清晰、更符合分层思想。
二、为什么要用 controller 包
2.1 问题:main.go 越来越臃肿
当所有路由和处理逻辑都堆在 main.go 中时:
func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.HTML(200, "index.html", nil)
})
r.GET("/login", func(c *gin.Context) {
c.HTML(200, "login.html", nil)
})
r.POST("/login", func(c *gin.Context) {
// 登录逻辑...
})
// 功能越多,这里越长
r.Run(":8080")
}
老师指出:随着页面和接口变多,
main.go会膨胀到几百行,既不好读也不好改。我们需要把"处理请求的逻辑"单独拿出来。
2.2 分层思想
把请求处理从主程序里拆出来,就是后端开发中常见的 MVC / 分层 思想:
| 层 | 职责 |
|---|---|
main.go(入口) |
初始化、注册路由 |
controller(控制器) |
接收请求、调用逻辑、返回响应 |
model(模型,后续) |
数据结构与数据库操作 |
view(视图,模板) |
页面展示 |
老师强调:把 handler 放进 controller 包,是为了职责分离。入口只负责"接线",具体的业务处理交给 controller。
三、创建 controller 包
3.1 新建目录与文件
在项目中新建 controller 目录,并创建 controller/index.go(或按模块拆分多个文件):
项目结构
├── main.go
├── go.mod
├── templates/
│ └── index.html
└── controller/
└── index.go
3.2 在 controller 中定义处理函数
controller 包里的处理函数,本质上和之前写在 main.go 里的匿名函数一样,只是变成了具名函数,并且首字母大写以便包外(main)可以调用:
// controller/index.go
package controller
import "github.com/gin-gonic/gin"
// Index 处理首页请求
func Index(c *gin.Context) {
c.HTML(200, "index.html", gin.H{
"title": "BBS首页",
})
}
// LoginPage 处理登录页请求
func LoginPage(c *gin.Context) {
c.HTML(200, "login.html", nil)
}
关键点:处理函数签名必须是
func(c *gin.Context),这和 Gin 注册路由时要求的 handler 类型完全一致。函数名大写才能被main包引用。
3.3 关于包名与可见性
| 写法 | 可见性 | 能否被 main 调用 |
|---|---|---|
func Index(...) |
大写,导出 | ✅ 可以 |
func index(...) |
小写,包内私有 | ❌ 不可以 |
老师提醒:Go 没有
public/private关键字,靠首字母大小写控制可见性。controller 要被main调用,函数必须大写开头。
四、在 main.go 中注册 controller
把处理函数抽到 controller 后,main.go 只负责"接线":
// main.go
package main
import (
"github.com/gin-gonic/gin"
"你的模块名/controller"
)
func main() {
r := gin.Default()
// 加载模板
r.LoadHTMLGlob("templates/*")
// 注册路由,引用 controller 包里的函数
r.GET("/", controller.Index)
r.GET("/login", controller.LoginPage)
r.Run(":8080")
}
老师说明:这里
controller.Index是函数本身(不是调用结果),Gin 会在请求到来时自动调用它并传入*gin.Context。
五、按模块拆分多个 controller 文件
当功能更多时,可以一个模块一个文件,都放在 controller 包下:
controller/
├── index.go // 首页相关
├── user.go // 用户相关(登录、注册)
└── post.go // 帖子相关
它们的 package 都写 controller,Go 会把同一个目录下的文件视为同一个包,互相之间可以直接调用。
// controller/user.go
package controller
import "github.com/gin-gonic/gin"
// Login 处理登录提交
func Login(c *gin.Context) {
username := c.PostForm("username")
// 处理登录逻辑...
c.JSON(200, gin.H{"username": username})
}
六、本讲小结
| 知识点 | 要点 |
|---|---|
| 为什么抽 controller | main.go 臃肿,需职责分离 |
| 处理函数签名 | func(c *gin.Context) |
| 导出规则 | 函数名大写才能被 main 调用 |
| 注册方式 | r.GET("/", controller.Index) 传函数本身 |
| 多文件同包 | 同目录同 package,可互相调用 |
| 分层思想 | 入口只接线,业务逻辑放 controller |
七、课后练习
- 把之前写在
main.go里的所有路由处理函数,抽到一个controller包中 - 新建
controller/user.go,写一个Login处理函数,返回 JSON 形式的用户名 - 在
main.go中引入 controller 包,并把路由改成controller.函数名的形式 - 思考:如果一个处理函数只在 controller 包内部使用,应该大写还是小写开头?为什么?
- 尝试按模块(首页 / 用户 / 帖子)把 controller 拆成多个文件,观察它们如何共享同一个包名
- 启动项目,访问首页和登录页,确认拆分后功能正常