💜 想真正擅长编程? 从一门课程开始,100% 免费

《笔记》第二十四篇 Gin设置controller包

第24讲 Gin设置controller包

本讲基于视频实际讲解内容整理


一、本讲概述

在前几讲中,我们的 Gin 路由和处理函数都写在 main.go 里。随着功能增多,这种做法会让 main.go 变得越来越臃肿、难以维护。本讲讲解如何把处理函数(Handler)抽离到独立的 controller(控制器)包 中,让项目结构更清晰、更符合分层思想。


二、为什么要用 controller 包

2.1 问题:main.go 越来越臃肿

当所有路由和处理逻辑都堆在 main.go 中时:

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(或按模块拆分多个文件):

TEXT
项目结构
├── main.go
├── go.mod
├── templates/
│   └── index.html
└── controller/
    └── index.go

3.2 在 controller 中定义处理函数

controller 包里的处理函数,本质上和之前写在 main.go 里的匿名函数一样,只是变成了具名函数,并且首字母大写以便包外(main)可以调用:

Go
// 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 只负责"接线":

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 包下:

TEXT
controller/
├── index.go      // 首页相关
├── user.go       // 用户相关(登录、注册)
└── post.go       // 帖子相关

它们的 package 都写 controller,Go 会把同一个目录下的文件视为同一个包,互相之间可以直接调用。

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

七、课后练习

  1. 把之前写在 main.go 里的所有路由处理函数,抽到一个 controller 包中
  2. 新建 controller/user.go,写一个 Login 处理函数,返回 JSON 形式的用户名
  3. 在 main.go 中引入 controller 包,并把路由改成 controller.函数名 的形式
  4. 思考:如果一个处理函数只在 controller 包内部使用,应该大写还是小写开头?为什么?
  5. 尝试按模块(首页 / 用户 / 帖子)把 controller 拆成多个文件,观察它们如何共享同一个包名
  6. 启动项目,访问首页和登录页,确认拆分后功能正常