为什么选择Gin框架?

Gin 是一个基于 Go 语言的高性能 Web 框架,具备以下优势:

  • 轻量高效:底层依赖 net/http,性能接近原生。
  • 简洁优雅:API 设计友好,支持路由分组、中间件链、参数绑定等特性。
  • 生态丰富:内置 JSON 解析、日志记录、错误恢复等实用功能,社区插件生态完善。
  • 无论是构建 RESTful API 还是全栈应用,Gin 都能显著提升开发效率。

安装

要安装Gin软件包,您需要安装Go并首先设置Go工作区。

  • 首先需要安装Go(需要1.10+版本),然后可以使用下面的Go命令安装Gin。
go get -u github.com/gin-gonic/gin
  • 1.
  • 将其导入您的代码中:
import "github.com/gin-gonic/gin"
  • 1.

基础示例

package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

func main() {
    // 1.创建 (实例化gin.Engine结构体对象)
    r := gin.Default()
    // 2.绑定路由规则,执行的函数
    // gin.Context,封装了request和response
    r.GET("/", func(c *gin.Context) {
        c.String(http.StatusOK, "Hello, Gin!")
    })
    // 3.监听端口,默认在8080
    //  Run("里面不指定端口号默认为8080")
    r.Run(":8080")
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

运行后访问 http://localhost:8080,即可看到返回的字符串。

Gin工工作作流流程

核心概念
  • Engine 容器对象,整个框架的基础
  • Engine.tree 负责存储路由和handle方法的映射,采用类似于字典树的结构
  • Engine.RouterGroup 其中handlers存储着所有中间件
  • Context 上下文对象,负责处理 请求回应 ,其中handles的存储处理请求时中间件和处理方法的

Gin框架从入门到实战:核心用法与最佳实践_gin

请求处理 流程

Gin框架从入门到实战:核心用法与最佳实践_gin_02

GIN启动流程
Gin初始化----> Use 中间件  ---->  注册Routers路由 ----> RUN()启动
  • 1.
Gin原原理解析

参考资料:http://v5blog.cn/pages/dd7d5a/

gin.Default()

Default()跟New()几乎一模一样, 就是调用了gin内置的Logger(), Recovery()中间件

// Default返回一个已经附加了Logger和Recovery中间件的Engine实例
func Default() *Engine {
	debugPrintWARNINGDefault()
	engine := New() "# 默认实例
	// 注册中间建,中间件的是一个函数,最终只要返回一个 type HandlerFunc func(*Context) 就可以
	engine.Use(Logger(), Recovery()) // 默认注册的两个中间件
	return engine
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

engine := New() 初初始化始化

通过调用 gin.New() 方法来实例化 Engine容器

engine.Use() 注注册册中间件 中间件

Gin框架从入门到实战:核心用法与最佳实践_中间件_03

路由与参数处理

无参路由
func HelloWorldhandler(ctx *gin.Context){
    ctx.JSON(200, gin.H{
        "message": "Hello World",
    })
}

func main() {
    // 创建一个默认的路由器
    router := gin.Default()
    // gin.Context是一个结构体,包含了请求和响应的细节, 封装request和response
    router.GET("/",HelloWorldhandler)
    // 路由重定向
    router.GET("/re", func(c *gin.Context) {
        c.Redirect(http.StatusMovedPermanently, "https://www.baidu.com")
    })
    // 启动服务器
    router.Run(":8081")
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
动态路由参数

通过 :param 捕获 URL 中的变量:
(可以通过Context的Param方法来获取API参数)

r.GET("/book/:id", func(c *gin.Context) {
    bookID := c.Param("id")
    c.String(http.StatusOK, "书籍ID: %s", bookID)
})
  • 1.
  • 2.
  • 3.
  • 4.
查询参数

使用 Query 或 DefaultQuery 获取 URL 参数:  http://127.0.0.1:8000/user?name=zhangsan

r.GET("/user", func(c *gin.Context) {
    name := c.Query("name")
    role := c.DefaultQuery("role", "guest")
    c.JSON(200, gin.H{"name": name, "role": role})
})
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
ShouldBind参数绑定

通过 ShouldBind 自动解析请求体(支持 JSON、Form 等):

我们可以基于请求的 Content-Type 识别请求数据类型并利用反射机制
自动提取请求中 QueryString 、 form表单 、 JSON 、 XML 等参数到结构体中

type LoginForm struct {
    Username string `form:"username" binding:"required"`
    Password string `form:"password" binding:"required"`
}

r.POST("/login", func(c *gin.Context) {
    var form LoginForm
    if err := c.ShouldBind(&form); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    c.String(200, "登录成功: %s", form.Username)
})
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
完整代码案例
package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "net/http"
)

// HelloWorldhandler 无参数路由处理函数
func HelloWorldhandler(ctx *gin.Context){
    ctx.JSON(200, gin.H{
        "message": "Hello World",
    })
}

func GetBookDetailHandler(ctx *gin.Context )  {
    bookId := ctx.Param("id")

    ctx.String(http.StatusOK, fmt.Sprintf("成功获取书籍详情:%s", bookId))

}

func GetUserDetailHandlers(ctx *gin.Context)   {
    username := ctx.Query("username")
    ctx.String(http.StatusOK, fmt.Sprintf("成功获取用户详情:%s", username))
}

type Login struct {
    Username string `form:"username" json:"username" binding:"required"`
    Password string `form:"password" json:"password" binding:"required"`
}

func ResponseJsonHandler(c *gin.Context) {
    type Data struct {
        Msg string `json:"msg:"`
        Code int    `json:"code"`
    }
    d := Data{
        Msg: "success",
        Code: 200,
    }
    c.JSON(http.StatusOK, d)
}
func Loginhandler(c *gin.Context)  {
    var login Login
    if err := c.ShouldBind(&login); err == nil {
        c.JSON(http.StatusOK, gin.H{
            "username": login.Username,
            "password": login.Password,
        })
    } else {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    }
}

func ResponseStringHandle(c *gin.Context)  {
    c.String(http.StatusOK, "Hello World")
}

func main() {
    // 创建一个默认的路由器
    router := gin.Default()
    // gin.Context是一个结构体,包含了请求和响应的细节, 封装request和response
    // 无参数路由
    router.GET("/",HelloWorldhandler)
    // 返回字符串
    router.GET("/string", ResponseStringHandle)
    // 返回json
    router.GET("/json", ResponseJsonHandler)
    // 动态路由参数
    router.GET("/book/:id", GetBookDetailHandler)
    // 查询参数 Query 或 DefaultQuery
    router.GET("/user/", GetUserDetailHandlers)
    // 参数绑定
    router.POST("/login", Loginhandler)
    // 路由重定向
    router.GET("/re", func(c *gin.Context) {
        c.Redirect(http.StatusMovedPermanently, "https://www.baidu.com")
    })
    // 启动服务器
    router.Run(":8081")
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
路由分发

为什么需要路由分发?

  • 我们一个项目有非常多的模块,如果全部写在一块导致代码结构混乱,不利于后续的扩展
  • 按照大的模块,每个模块有自己独立的路由,主路由可以再main.go中进行注册
项目结构
├── go.mod
├── go.sum
├── main.go
└── routers
  ├── books.go
  └── users.go
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

main.go

import (
"days/routers"
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
    router := gin.Default()
    // 全局中间件
    router.Use(MiddleWare())
    // 加载路由
    routers.LoadUsers(router)
    routers.LoadBooks(router)
    router.Run(":8081")
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

routers/users.go

package routers
import (
    "fmt"
    "github.com/gin-gonic/gin"
    "net/http"
    "time"
)

func LoadUsers(e *gin.Engine)  {
    e.GET("/user",MiddleWareOne(),UserHandler)
}

func UserHandler(c *gin.Context) {

    fmt.Println("我是用户路由")
    time.Sleep(time.Second * 5)
    c.JSON(http.StatusOK,gin.H{
        "message" : "weclome user",
    })
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

routers/books.go

package routers
import (
"net/http"
"github.com/gin-gonic/gin"
)
func LoadBooks(e *gin.Engine) {
    e.GET("/book", GetBookHandler)
}
func GetBookHandler(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{
        "message": "Book Router",
    })
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

Gin框架从入门到实战:核心用法与最佳实践_中间件_04

中间件

Gin框架从入门到实战:核心用法与最佳实践_JSON_05

  • Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。
  • 这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑
  • 比如登录认证、权限校验、数据分页、记录日志、耗时统计等
全局中间件
func Logger() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        c.Next()
        latency := time.Since(start)
        fmt.Printf("请求耗时: %v\n", latency)
    }
}

func main() {
    r := gin.Default()
    r.Use(Logger()) // 全局生效
    r.GET("/", func(c *gin.Context) { /* ... */ })
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
局部中间件
func AuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("token")
        if token != "SECRET_KEY" {
            c.AbortWithStatusJSON(401, gin.H{"error": "身份验证失败"})
        }
        c.Next()
    }
}

r.GET("/profile", AuthMiddleware(), func(c *gin.Context) {
    c.JSON(200, gin.H{"data": "用户信息"})
})
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
next()方法
  • 在中间件中调用next()方法,会从next()方法调用的地方跳转到Handler函数
  • Handler函数执行完成,若中间件还有部分代码未执行(中间件中next()之后的代码),则执行该代码
package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "net/http"
    "time"
)

func main() {
    r := gin.Default()
    r.Use(Log(), RequestID())
    r.GET("/", func(c *gin.Context) {
        fmt.Println("app running  1")
        time.Sleep(time.Second * 5)
        c.String(http.StatusOK, "hello World!")
    })
    r.Run()
}
func Log() gin.HandlerFunc {
    return func(c *gin.Context) {
        fmt.Println("log start")
        c.Next()
        fmt.Println("log end")
    }
}
func RequestID() gin.HandlerFunc {
    return func(c *gin.Context) {
        fmt.Println("requestid start")
        c.Next()
        fmt.Println("requestid end")
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
实现token认证
  • http://127.0.0.1:8080/index index首页无需token直接访问
  • http://127.0.0.1:8080/home home家目录需要对token进行验证,验证通过才可访问
package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
)

func AuthMiddleWare() func(c *gin.Context)  {

    return func(c *gin.Context){
        // 客户端携带token 有三种方式 1.放在请求头 2.放在请求体 3.放在url
        // token 验证成功,返回 c.next()才会继续,否则 c.Abort()
        token := c.Request.Header.Get("token")
        fmt.Println("获取token:",token)
        if token == "" {
            c.JSON(200, gin.H{
                "code": 401,
                "msg": "身份验证不通过",
            })
            c.Abort()
        }
        if token != "123456" {
            c.JSON(200, gin.H{
                "code": 401,
                "msg": "token错误",
            })
            c.Abort()
        }

    }
}





func main()  {
    router := gin.Default()
    // 首页无需验证
    router.GET("/", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "首页",
        })
    })
    // Home页面需要验证
    router.GET("/home", AuthMiddleWare(), func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "Home页面",
        })
    })
    router.Run(":8081")
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.

Gin框架从入门到实战:核心用法与最佳实践_JSON_06