gin 框架

gin 框架介绍

一、Gin 是什么?

Gin 是 Go 语言的一个 Web 框架,主要用于开发:

  • Web 网站

  • RESTful API

  • 后端服务

  • 微服务 HTTP 接口

  • 前后端分离项目的后端

  • 网关/API Gateway

Go 原生其实就可以写 Web 服务:

http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "pong")
})

http.ListenAndServe(":8080", nil)

但是项目一复杂,就会遇到:

  • 路由管理

  • 参数解析

  • JSON 返回

  • 中间件

  • 请求上下文

  • 错误处理

  • 参数校验

  • 日志

  • JWT 鉴权

所以 Gin 在 Go 原生 net/http 的基础上,提供了一套更加方便的开发方式。

可以粗略理解:

Go
│
├── net/http        ← Go 原生 HTTP 能力
│
└── Gin             ← 在 net/http 基础上的 Web 框架

二、Gin 最核心的东西:路由

你之前看到的代码:

r.GET("/ping", func(c *gin.Context) {

    c.JSON(http.StatusOK, gin.H{
        "message": "pong",
    })

})

这其实就是 Gin 最核心的使用方式。

拆开:

r.GET(
    "/ping",
    func(c *gin.Context) {
        ...
    },
)

意思是:

当客户端使用 GET 请求访问 /ping 时,就执行这个函数。

所以:

浏览器
   │
   │ GET /ping
   ↓
 Gin
   │
   │ 匹配 /ping
   ↓
func(c *gin.Context) {
    ...
}
   │
   ↓
返回 JSON

这里你之前困惑的:

为什么传函数,而不是执行函数?

就是因为:

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

是一个函数值

你是在告诉 Gin:

“以后有人访问 /ping,你帮我调用这个函数。”

而不是现在就调用。

如果写成:

r.GET("/ping", handler())

那就是现在立刻执行 handler(),这和 Gin 需要的逻辑完全不同。


三、Gin 的 Context 非常重要

Gin 中你会大量看到:

func(c *gin.Context) {
    
}

这个 c 就是:

*gin.Context

可以把它理解成:

一次 HTTP 请求的“上下文对象”。

它里面包含了大量这次请求相关的信息。

例如:

1. 获取 URL 参数

请求:

GET /user/123

路由:

r.GET("/user/:id", func(c *gin.Context) {

    id := c.Param("id")

    fmt.Println(id)
})

得到:

123

2. 获取 Query 参数

请求:

GET /user?id=123

代码:

id := c.Query("id")

得到:

123

3. 获取 POST JSON

客户端:

{
    "username": "张三",
    "age": 20
}

  

Go:

type User struct {
    Username string `json:"username"`
    Age      int    `json:"age"`
}

var user User

err := c.ShouldBindJSON(&user)

  

然后:

user.Username
user.Age

  

就可以拿到数据。


四、Gin 返回 JSON 非常方便

你之前看到:

c.JSON(http.StatusOK, gin.H{
    "message": "pong",
})

  

返回:

{
    "message": "pong"
}

其中:

gin.H

本质上就是:

map[string]interface{}

所以:

gin.H{
    "message": "pong",
}

可以理解成:

map[string]interface{}{
    "message": "pong",
}

  


五、Gin 的 Middleware(中间件)

这是 Gin 非常重要的一个概念。

例如你有:

客户端
   ↓
日志
   ↓
JWT鉴权
   ↓
权限检查
   ↓
业务 Handler
   ↓
数据库
这些“中间环节”就是 Middleware。

例如:

func Logger() gin.HandlerFunc {

    return func(c *gin.Context) {

        fmt.Println("请求进入")

        c.Next()

        fmt.Println("请求结束")
    }
}
然后:

r.Use(Logger())
那么请求:

GET /user
就可能变成:

Logger
   ↓
User Handler
   ↓
Logger
这和你前面学习的 **gRPC Interceptor(拦截器)**其实非常像。

你可以建立这样的对应关系:

Gin

Middleware
     ↓
拦截 HTTP 请求
     ↓
执行 Handler
     ↓
返回响应
而 gRPC:

Interceptor
     ↓
拦截 RPC 请求
     ↓
执行 RPC 方法
     ↓
返回响应
所以现在学 Gin,实际上也会帮助理解中间件、请求生命周期、拦截器这些后端核心概念。

  


六、Gin 的路由分组

实际项目不会把所有接口都写在一起。

比如:

api := r.Group("/api")
{
    api.GET("/user", getUser)
    api.POST("/user", createUser)
}
那么:

GET  /api/user
POST /api/user
都属于 /api。

还可以继续分:

api := r.Group("/api")

v1 := api.Group("/v1")

v1.GET("/user", getUser)
v1.POST("/user", createUser)
最终:

/api/v1/user
这在大型项目里非常常见。

  


七、Gin 一个完整的小例子

package main

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

func main() {

    r := gin.Default()

    r.GET("/ping", func(c *gin.Context) {

        c.JSON(http.StatusOK, gin.H{
            "message": "pong",
        })
    })

    r.GET("/user/:id", func(c *gin.Context) {

        id := c.Param("id")

        c.JSON(http.StatusOK, gin.H{
            "id": id,
        })
    })

    r.Run(":8080")
}
启动:

服务器监听 8080
访问:

GET /ping
得到:

{
    "message": "pong"
}
访问:

GET /user/123
得到:

{
    "id": "123"
}

  


八、Gin 项目一般怎么组织?

实际项目一般不会把所有东西塞进 main.go

例如:

mall/
│
├── main.go
│
├── router/
│   └── router.go
│
├── handler/
│   ├── user.go
│   └── product.go
│
├── service/
│   ├── user.go
│   └── product.go
│
├── model/
│   ├── user.go
│   └── product.go
│
├── middleware/
│   ├── auth.go
│   └── logger.go
│
└── config/
    └── config.go

  

大概形成:

HTTP请求
   ↓
Gin Router
   ↓
Middleware
   ↓
Handler
   ↓
Service
   ↓
Repository / DAO
   ↓
Database

  

这就是你以后写真正业务系统时非常重要的一条链路。


九、Gin 和 gRPC 是什么关系?

这个特别值得你现在理解。

Gin 和 gRPC 并不是竞争关系。

它们解决的问题不同:

                后端系统
                   │
       ┌───────────┴───────────┐
       ↓                       ↓
     Gin                     gRPC
       │                       │
    HTTP/REST              RPC通信
       │                       │
   前端/浏览器              微服务之间

  

比如你的电商系统:

Vue
 │
 │ HTTP
 ↓
Gin API
 │
 │ gRPC
 ↓
商品服务
 │
 ├── 商品数据库
 │
 └── Redis

  

所以完全可以:

Vue
 ↓
Gin
 ↓
gRPC
 ↓
User Service
Product Service
Order Service

  

这其实就是非常典型的:

Gin + gRPC + Redis + MySQL + Kubernetes

的微服务技术栈。


十、现在最应该掌握 Gin 的哪些东西?

如果正在从 Go 基础往后端开发走,建议按照这个顺序:

① HTTP 基础
      ↓
② Gin 路由
      ↓
③ gin.Context
      ↓
④ GET / POST 参数
      ↓
⑤ JSON 请求与响应
      ↓
⑥ 路由分组
      ↓
⑦ Middleware
      ↓
⑧ 参数校验
      ↓
⑨ JWT 鉴权
      ↓
⑩ 文件上传
      ↓
⑪ 全局错误处理
      ↓
⑫ Gin + MySQL
      ↓
⑬ Gin + Redis
      ↓
⑭ Gin + gRPC
      ↓
⑮ Gin + Kubernetes

  

你现在尤其应该把 Handler → Middleware → Context → Router 这几个概念吃透。

因为你前面刚好在问:

“Gin 为什么传匿名函数?”

其实一旦把 “注册路由 = 把函数交给框架,等请求到来后由框架调用” 这个思想真正理解了,Gin 后面的很多东西都会突然顺起来。

 

 gin 的入门

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)
func pong(c *gin.Context){
	c.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
}
func main() {
	//实例化一个gin的server对象
	r := gin.Default()
	r.GET("/ping", pong)
	r.Run(":8083") // listen and serve on 0.0.0.0:8080
}
启动
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> main.pong (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8083
[GIN] 2026/08/28 - 13:01:30 | 200 |       0s |       127.0.0.1 | GET      "/ping"

  gin 的路由分组

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)
func goodsList(c *gin.Context){
	c.JSON(http.StatusOK,gin.H{
		"name":"goodsList",
	})
}
func goodsDetail(c *gin.Context){}
func createGoods(c *gin.Context){}

func main() {
	router := gin.Default()
	goodsGroup:=router.Group("/goods")//路由分组
	//路由分组;{}这对括号可有可无
	{
		goodsGroup.GET("/list",goodsList)
		goodsGroup.GET("/1",goodsDetail)
		goodsGroup.POST("/add",createGoods)
	}
	// router.GET("/goods/list",goodsList)
	// router.GET("/goods/1",goodsDetail)
	// router.POST("/goods/add",createGoods)
	// v1:= router.Group("/v1")
	// {
	// 	v1.POST("/login",loginEndpoint)
	// 	v1.POST("/submit",submitEndpoint)
	// 	v1.POST("/read",readEndpoint)
	// }
	// v2:= router.Group("/v2")
	// {
	// 	v2.POST("/login",loginEndpoint)
	// 	v2.POST("/submit",submitEndpoint)
	// 	v2.POST("/read",readEndpoint)		
	// }
	router.Run(":8081")//执行端口;如果不指定默认是8080
}
启动
PS D:\golang\goproject\src\lnhgo> go run gin\ch01\main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /goods/list               --> main.goodsList (3 handlers)
[GIN-debug] GET    /goods/1                  --> main.goodsDetail (3 handlers)
[GIN-debug] POST   /goods/add                --> main.createGoods (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8081
[GIN] 2026/08/28 - 14:13:23 | 404 |       0s |       127.0.0.1 | GET      "/list"
[GIN] 2026/08/28 - 14:13:24 | 404 |       0s |       127.0.0.1 | GET      "/favicon.ico"
[GIN] 2026/08/28 - 14:13:54 | 200 |       0s |       127.0.0.1 | GET      "/goods/list"

  gin 处理带参数的url变量

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)
func goodsList(c *gin.Context){
	c.JSON(http.StatusOK,gin.H{
		"name":"goodsList",
	})
}
func goodsDetail(c *gin.Context){
	id:=c.Param("id")//获取变量的值
	c.JSON(http.StatusOK,gin.H{
		"name":id,
	})

}
func createGoods(c *gin.Context){}

func main() {
	router := gin.Default()
	goodsGroup:=router.Group("/goods")//路由分组
	//路由分组;{}这对括号可有可无
	{
		goodsGroup.GET("/list",goodsList)
		goodsGroup.GET("/:id",goodsDetail)//获取变量匹配
		goodsGroup.POST("/add",createGoods)
	}
	// router.GET("/goods/list",goodsList)
	// router.GET("/goods/1",goodsDetail)
	// router.POST("/goods/add",createGoods)
	// v1:= router.Group("/v1")
	// {
	// 	v1.POST("/login",loginEndpoint)
	// 	v1.POST("/submit",submitEndpoint)
	// 	v1.POST("/read",readEndpoint)
	// }
	// v2:= router.Group("/v2")
	// {
	// 	v2.POST("/login",loginEndpoint)
	// 	v2.POST("/submit",submitEndpoint)
	// 	v2.POST("/read",readEndpoint)		
	// }
	router.Run(":8081")//执行端口;如果不指定默认是8080
}
启动

PS D:\golang\goproject\src\lnhgo> go run gin\ch01\main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /goods/list               --> main.goodsList (3 handlers)
[GIN-debug] GET    /goods/:id                --> main.goodsDetail (3 handlers)
[GIN-debug] POST   /goods/add                --> main.createGoods (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8081
[GIN] 2026/08/28 - 15:49:04 | 200 |       0s |       127.0.0.1 | GET      "/goods/6"
[GIN] 2026/08/28 - 15:49:14 | 200 |       0s |       127.0.0.1 | GET      "/goods/a"
[GIN] 2026/08/28 - 15:49:22 | 200 |       0s |       127.0.0.1 | GET      "/goods/list"
[GIN] 2026/08/28 - 15:49:29 | 200 |       0s |       127.0.0.1 | GET      "/goods/a"

  获取url 多个变量

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)
func goodsList(c *gin.Context){
	c.JSON(http.StatusOK,gin.H{
		"name":"goodsList",
	})
}
func goodsDetail(c *gin.Context){
	id:=c.Param("id")
	action:=c.Param("action")//多个变量

	c.JSON(http.StatusOK,gin.H{
		"name":id,
		"action":action,
	})

}
func createGoods(c *gin.Context){}

func main() {
	router := gin.Default()
	goodsGroup:=router.Group("/goods")//路由分组
	//路由分组;{}这对括号可有可无
	{
		goodsGroup.GET("/list",goodsList)
		goodsGroup.GET("/:id/:action",goodsDetail) //多个变量
		goodsGroup.POST("/add",createGoods)
	}
	// router.GET("/goods/list",goodsList)
	// router.GET("/goods/1",goodsDetail)
	// router.POST("/goods/add",createGoods)
	// v1:= router.Group("/v1")
	// {
	// 	v1.POST("/login",loginEndpoint)
	// 	v1.POST("/submit",submitEndpoint)
	// 	v1.POST("/read",readEndpoint)
	// }
	// v2:= router.Group("/v2")
	// {
	// 	v2.POST("/login",loginEndpoint)
	// 	v2.POST("/submit",submitEndpoint)
	// 	v2.POST("/read",readEndpoint)		
	// }
	router.Run(":8081")//执行端口;如果不指定默认是8080
}
启动
PS D:\golang\goproject\src\lnhgo> go run gin\ch01\main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /goods/list               --> main.goodsList (3 handlers)
[GIN-debug] GET    /goods/:id/:action        --> main.goodsDetail (3 handlers)
[GIN-debug] POST   /goods/add                --> main.createGoods (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8081
[GIN] 2026/08/28 - 16:01:32 | 200 |       0s |       127.0.0.1 | GET      "/goods/a/d"

  url 里的* 使用(前后匹配所有)

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)
func goodsList(c *gin.Context){
	c.JSON(http.StatusOK,gin.H{
		"name":"goodsList",
	})
}
func goodsDetail(c *gin.Context){
	id:=c.Param("id")
	action:=c.Param("action")

	c.JSON(http.StatusOK,gin.H{
		"name":id,
		"action":action,
	})

}
func createGoods(c *gin.Context){}

func main() {
	router := gin.Default()
	goodsGroup:=router.Group("/goods")//路由分组
	//路由分组;{}这对括号可有可无
	{
		goodsGroup.GET("/list",goodsList)
		goodsGroup.GET("/:id/*action",goodsDetail)
		goodsGroup.POST("/add",createGoods)
	}
	
	router.Run(":8081")//执行端口;如果不指定默认是8080
}
启动
PS D:\golang\goproject\src\lnhgo> go run gin\ch01\main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /goods/list               --> main.goodsList (3 handlers)
[GIN-debug] GET    /goods/:id/*action        --> main.goodsDetail (3 handlers)
[GIN-debug] POST   /goods/add                --> main.createGoods (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8081
[GIN] 2026/08/28 - 16:14:24 | 200 |       0s |       127.0.0.1 | GET      "/goods/a/d/u"
[GIN] 2026/08/28 - 16:14:48 | 200 |       0s |       127.0.0.1 | GET      "/goods/a/d/u"

  浏览器效果

image

强约定数字

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

type Person struct{
	ID string `uri:"id" binding:"required,uuid"`//强约定uuid
	Name string `uri:"name" binding:"required"`
}

func main() {
	router := gin.Default()
	router.GET("/:name/:id",func(c *gin.Context) {
		var person Person
		if err:= c.ShouldBindUri(&person);err!=nil{
			c.Status(404)
			return 
		}
		c.JSON(http.StatusOK,gin.H{
			"name":person.Name,
			"id":person.ID,
		})
	})
	router.Run(":8015")

}
启动
PS D:\golang\goproject\src\lnhgo> go run gin\ch03\main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /:name/:id                --> main.main.func1 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8015
[GIN] 2026/08/28 - 17:22:11 | 404 |       0s |       127.0.0.1 | GET      "/goods/6"
[GIN] 2026/08/28 - 17:24:13 | 200 |       0s |       127.0.0.1 | GET      "/goods/ccce7edd-329b-4f12-bee0-92566b51d833"
[GIN] 2026/08/28 - 17:24:13 | 404 |       0s |       127.0.0.1 | GET      "/favicon.ico"

  id 那边改成int 类型数字

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

type Person struct{
	ID int `uri:"id" binding:"required"`
	Name string `uri:"name" binding:"required"`
}

func main() {
	router := gin.Default()
	router.GET("/:name/:id",func(c *gin.Context) {
		var person Person
		if err:= c.ShouldBindUri(&person);err!=nil{
			c.Status(404)
			return 
		}
		c.JSON(http.StatusOK,gin.H{
			"name":person.Name,
			"id":person.ID,
		})
	})
	router.Run(":8015")

}


启动测试
PS D:\golang\goproject\src\lnhgo> go run gin\ch03\main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /:name/:id                --> main.main.func1 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8015
[GIN] 2026/08/28 - 17:32:06 | 200 |  551.7µs |       127.0.0.1 | GET      "/goods/6"

  获取get参数

package main
import (
	"net/http"

	"github.com/gin-gonic/gin"
)
func main() {
	router := gin.Default()
	//匹配的url格式:/welcome?firstname=Jane&lastname=Doe
	router.GET("/welcome",func(c *gin.Context) {
		firstname := c.DefaultQuery("firstname","Guest")
		lastname := c.Query("lastname")//是c.Request.URL.Query().Get("lastname")的
		c.String(http.StatusOK,"Hello %s %s",firstname,lastname)
	})
	router.Run(":8084")
}

  获取post

 

package main
import (
	"net/http"

	"github.com/gin-gonic/gin"
)
func main() {
	router := gin.Default()
	//匹配的url格式:/welcome?firstname=Jane&lastname=Doe
	router.GET("/welcome",func(c *gin.Context) {
		firstname := c.DefaultQuery("firstname","Guest")
		//lastname := c.Query("lastname")//是c.Request.URL.Query().Get("lastname")
		lastname:=c.DefaultQuery("lastname","imooc")
		// c.String(http.StatusOK,"Hello %s %s",firstname,lastname)
		c.JSON(http.StatusOK,gin.H{
			"first_name":firstname,
			"last_name":lastname,
		})
	})
	router.Run(":8084")
}
启动
PS D:\golang\goproject\src\lnhgo> go run gin\ch04\main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /welcome                  --> main.main.func1 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8084
[GIN] 2026/08/30 - 17:10:52 | 200 |  507.1µs |       127.0.0.1 | GET      "/welcome"
[GIN] 2026/08/30 - 17:10:53 | 404 |       0s |       127.0.0.1 | GET      "/favicon.ico"
[GIN] 2026/08/30 - 17:12:08 | 200 |       0s |       127.0.0.1 | GET      "/welcome?firstname=%E6%85%95%E8%AF%BE%E7%BD%91&last_name=bool"
[GIN] 2026/08/30 - 17:12:40 | 200 |       0s |       127.0.0.1 | GET      "/welcome?firstname=%E6%85%95%E8%AF%BE%E7%BD%91&lastname=bool"

  测试

image

http://127.0.0.1:8084/welcome?firstname=cx&lastname=bool

image

post 请求参数

package main
import (
	"net/http"

	"github.com/gin-gonic/gin"
)
func main() {
	router := gin.Default()
	//匹配的url格式:/welcome?firstname=Jane&lastname=Doe
	router.GET("/welcome",func(c *gin.Context) {
		firstname := c.DefaultQuery("firstname","Guest")
		//lastname := c.Query("lastname")//是c.Request.URL.Query().Get("lastname")
		lastname:=c.DefaultQuery("lastname","imooc")
		// c.String(http.StatusOK,"Hello %s %s",firstname,lastname)
		c.JSON(http.StatusOK,gin.H{
			"first_name":firstname,
			"last_name":lastname,
		})
	})
	router.POST("/form_post",func(c *gin.Context) {
		message:=c.PostForm("message")
		nick:= c.DefaultPostForm("nike","anonymous")
		c.JSON(http.StatusOK,gin.H{
			"message":message,
			"nick":nick,
		})
	})
	router.Run(":8084")
}
启动
PS D:\golang\goproject\src\lnhgo> go run gin\ch04\main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /welcome                  --> main.main.func1 (3 handlers)
[GIN-debug] POST   /form_post                --> main.main.func2 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8084
[GIN] 2026/08/30 - 17:35:57 | 404 |       0s |       127.0.0.1 | POST     "/"
[GIN] 2026/08/30 - 17:38:03 | 404 |       0s |       127.0.0.1 | POST     "/from_post?message=%E4%BD%A0%E5%A5%BD&nick=bobby"
[GIN] 2026/08/30 - 17:38:12 | 404 |       0s |       127.0.0.1 | POST     "/from_post?message=%E4%BD%A0%E5%A5%BD&nick=bobby"
[GIN] 2026/08/30 - 17:38:13 | 404 |       0s |       127.0.0.1 | POST     "/from_post?message=%E4%BD%A0%E5%A5%BD&nick=bobby"
[GIN] 2026/08/30 - 17:38:53 | 200 |       0s |       127.0.0.1 | POST     "/form_post?message=%E4%BD%A0%E5%A5%BD&nick=bobby"
[GIN] 2026/08/30 - 17:38:58 | 200 |       0s |       127.0.0.1 | POST     "/form_post?message=%E4%BD%A0%E5%A5%BD&nick=bobby"
[GIN] 2026/08/30 - 17:40:30 | 200 |       0s |       127.0.0.1 | POST     "/form_post"

  工具postman测试

image

 混合 post 与get

package main
import (
	"net/http"

	"github.com/gin-gonic/gin"
)
func main() {
	router := gin.Default()
	//匹配的url格式:/welcome?firstname=Jane&lastname=Doe
	router.GET("/welcome",func(c *gin.Context) {
		firstname := c.DefaultQuery("firstname","Guest")
		//lastname := c.Query("lastname")//是c.Request.URL.Query().Get("lastname")
		lastname:=c.DefaultQuery("lastname","imooc")
		// c.String(http.StatusOK,"Hello %s %s",firstname,lastname)
		c.JSON(http.StatusOK,gin.H{
			"first_name":firstname,
			"last_name":lastname,
		})
	})
	router.POST("/form_post",func(c *gin.Context) {
		message:=c.PostForm("message")
		nick:= c.DefaultPostForm("nike","anonymous")
		c.JSON(http.StatusOK,gin.H{
			"message":message,
			"nick":nick,
		})
	})
	router.POST("/post",func(c *gin.Context) {
		id := c.Query("id")//获取url参数
		page:=c.DefaultQuery("page","0")
		name:=c.PostForm("name")//获取表单里内容
		message:=c.DefaultPostForm("massage","信息")
		c.JSON(http.StatusOK,gin.H{
			"id":id,
			"page":page,
			"name":name,
			"message":message,
		})
	})
	router.Run(":8084")
}
启动
PS D:\golang\goproject\src\lnhgo> go run gin\ch04\main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /welcome                  --> main.main.func1 (3 handlers)
[GIN-debug] POST   /form_post                --> main.main.func2 (3 handlers)
[GIN-debug] POST   /post                     --> main.main.func3 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8084
[GIN] 2026/08/30 - 17:56:55 | 200 |  505.2µs |       127.0.0.1 | POST     "/post?id=1&page=2"

  工具postman测试1 url 参数

image

 pastman 2

image

 json、protobuf 渲染

syntax = "proto3";
option go_package = ".;proto";
message Teacher {
    string name =1;
    repeated string course =2;
    
}
生成文件
protoc --go_out=. --go-grpc_out=. user.proto 


gin 代码
package main

import (
	"net/http"

	"github.com/gin-gonic/gin"

	"lnhgo/gin/ch06/proto"
)

func main() {
	router := gin.Default()
	//匹配的url格式:/welcome?firstname=Jane&lastname=Doe
	router.GET("/moreJSON", moreJSON)
	router.GET("/someProtoBuf",returnProto)
	router.Run(":8015")
}
func moreJSON(c *gin.Context){
	var msg struct{
		Name string `json:"user"`
		Messages string 
		Number int
	}
	msg.Name="boobby"
	msg.Messages="这是一个测试"
	msg.Number= 20
	c.JSON(http.StatusOK,msg)
}
func returnProto(c *gin.Context){
	coures:=[]string{"python","go","微服务"}
	user:=&proto.Teacher{
		Name: "bobby",
		Course: coures,
	}
	c.ProtoBuf(http.StatusOK,user)
}
启动
PS D:\golang\goproject\src\lnhgo> go run gin\ch06\main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /moreJSON                 --> main.moreJSON (3 handlers)
[GIN-debug] GET    /someProtoBuf             --> main.returnProto (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8015
[GIN] 2026/08/31 - 15:23:51 | 200 |  530.5µs |       127.0.0.1 | GET      "/someProtoBuf"
[GIN] 2026/08/31 - 15:25:51 | 404 |       0s |       127.0.0.1 | GET      "/.well-known/appspecific/com.chrome.devtools.json"
[GIN] 2026/08/31 - 15:26:10 | 200 |       0s |       127.0.0.1 | GET      "/moreJSON"
[GIN] 2026/08/31 - 15:26:10 | 404 |       0s |       127.0.0.1 | GET      "/.well-known/appspecific/com.chrome.devtools.json"
[GIN] 2026/08/31 - 15:26:49 | 404 |       0s |       127.0.0.1 | GET      "/.well-known/appspecific/com.chrome.devtools.json"
[GIN] 2026/08/31 - 15:26:54 | 200 |       0s |       127.0.0.1 | GET      "/someProtoBuf"

  表单验证第三方库下载地址文档

PS D:\golang\goproject\src\lnhgo> go get github.com/go-playground/validator/v10
go: downloading github.com/go-playground/validator v9.31.0+incompatible
go: downloading github.com/go-playground/validator/v10 v10.30.3
go: downloading github.com/gabriel-vasile/mimetype v1.4.13
go: upgraded github.com/gabriel-vasile/mimetype v1.4.12 => v1.4.13
go: upgraded github.com/go-playground/validator/v10 v10.30.1 => v10.30.3

 若要将请求主体绑定到结构体中,请使用模型绑定,目前支持JSON、XML、YAML和标准表单值(foo=bar&boo=baz)的绑定。

需要在绑定的字段上设置tag,比如,绑定格式为json,需要这样设置 json:"fieldname"

此外,Gin还提供了两套绑定方法:

  • Must bind
    • Methods - Bind, BindJSON, BindXML, BindQuery, BindYAML
    • Behavior - 这些方法底层使用 MustBindWith,如果存在绑定错误,请求将被以下指令中止 c.AbortWithError(400, err).SetType(ErrorTypeBind),响应状态代码会被设置为400,请求头Content-Type被设置为text/plain; charset=utf-8。注意,如果你试图在此之后设置响应代码,将会发出一个警告 [GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422,如果你希望更好地控制行为,请使用ShouldBind相关的方法
  • Should bind
    • Methods - ShouldBind, ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAML
    • Behavior - 这些方法底层使用 ShouldBindWith,如果存在绑定错误,则返回错误,开发人员可以正确处理请求和错误。

当我们使用绑定方法时,Gin会根据Content-Type推断出使用哪种绑定器,如果你确定你绑定的是什么,你可以使用MustBindWith或者BindingWith

表单验证

package main

import (
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
)

type LoginForm struct {
	User     string `json:"user"  binding:"required,min=3,max=10"` //前端传递的模式;各种类型数据
	Password string `json:"password" binding:"required"`
}

func main() {
	routes := gin.Default()
	routes.POST("/loginJSON",func(c *gin.Context) {
		var loginFor LoginForm
		if err:=c.ShouldBind(&loginFor);err!=nil{
			fmt.Println(err)
			c.JSON(http.StatusBadRequest,gin.H{
				"error":err.Error(),
			})
			return 
		}
		c.JSON(http.StatusOK,gin.H{
			"mag":"登录成功",
		})
	})
	routes.Run(":8083")
}
启动
PS D:\golang\goproject\src\lnhgo> go run gin\ch07\main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] POST   /loginJSON                --> main.main.func1 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8083
Key: 'LoginForm.User' Error:Field validation for 'User' failed on the 'required' tag
Key: 'LoginForm.Password' Error:Field validation for 'Password' failed on the 'required' tag
[GIN] 2026/08/31 - 16:42:13 | 400 |  531.7µs |       127.0.0.1 | POST     "/loginJSON"
Key: 'LoginForm.User' Error:Field validation for 'User' failed on the 'required' tag
Key: 'LoginForm.Password' Error:Field validation for 'Password' failed on the 'required' tag
[GIN] 2026/08/31 - 16:42:19 | 400 |       0s |       127.0.0.1 | POST     "/loginJSON"

     登录测试

用户名失败不符合tge标准

image

 测试成功的

image

 

  

注册表单

 

package main

import (
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
)

type LoginForm struct {
	User     string `json:"user"  binding:"required,min=3,max=10"` //前端传递的模式;各种类型数据
	Password string `json:"password" binding:"required"`
}
type SignUpForm struct{
	Age uint8 `json:"age" binding:"required,gte=1,lte=130"`//表示大于1;或者小于130
	Name string `json:"name" binding:"required,min=3"`
	Email string `json:"email" binding:"required,email"`//强制满足email 格式
	Password string `json:"password" binding:"required"`
	RePassword string `json:"repassword" binding:"required,eqfield=Password"`//跨字段验证
}
func main() {
	routes := gin.Default()
	routes.POST("/loginJSON",func(c *gin.Context) {
		var loginFor LoginForm
		if err:=c.ShouldBind(&loginFor);err!=nil{
			fmt.Println(err.Error())
			c.JSON(http.StatusBadRequest,gin.H{
				"error":err.Error(),
			})
			return 
		}
		c.JSON(http.StatusOK,gin.H{
			"mag":"登录成功",
		})
	})
	routes.POST("/signup",func(c *gin.Context) {
		var SignUpForm SignUpForm
		if err := c.ShouldBind(&SignUpForm);err !=nil{
			fmt.Println(err.Error())
			c.JSON(http.StatusBadRequest,gin.H{
				"error":err.Error(),
			})
			return
		}
		c.JSON(http.StatusOK,gin.H{
			"msg":"登录成功",
		})
	})
	routes.Run(":8083")
}
启动
PS D:\golang\goproject\src\lnhgo> go run gin\ch07\main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] POST   /loginJSON                --> main.main.func1 (3 handlers)
[GIN-debug] POST   /signup                   --> main.main.func2 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8083

  验证两次密码输入不一致

image

 测试成功查看返回

image

 错误提示验证信息翻译成中文

package main

import (
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/locales/en"
	"github.com/go-playground/locales/zh"
	ut "github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
	enTranslations "github.com/go-playground/validator/v10/translations/en"
	zhTranslations "github.com/go-playground/validator/v10/translations/zh"
)
var trans ut.Translator
type LoginForm struct {
	User     string `json:"user"  binding:"required,min=3,max=10"` //前端传递的模式;各种类型数据
	Password string `json:"password" binding:"required"`
}
type SignUpForm struct{
	Age uint8 `json:"age" binding:"required,gte=1,lte=130"`//表示大于1;或者小于130
	Name string `json:"name" binding:"required,min=3"`
	Email string `json:"email" binding:"required,email"`
	Password string `json:"password" binding:"required"`
	RePassword string `json:"repassword" binding:"required,eqfield=Password"`//跨字段验证
}
func InintTrans(locale string)(err error){
	//修改gin框架中的validator引擎属性,实现定制
	if v,ok :=binding.Validator.Engine().(*validator.Validate);ok{
		zhT:= zh.New()//中文
		enT:=en.New()//英文翻译
		//第一个参数备用语言环境,后续参数应该支持的语言环境
		uni:=ut.New(enT,zhT,enT)
		if trans,ok =uni.GetTranslator(locale);!ok{
			return fmt.Errorf("GetTranslator(%s)",locale)
		}else{
			switch locale{
			case "en":
				enTranslations.RegisterDefaultTranslations(v,trans)
			case "zh":
				zhTranslations.RegisterDefaultTranslations(v,trans)
			default:
				enTranslations.RegisterDefaultTranslations(v,trans)

			}
			return 
		}
		//return

	}
	return 
}

func main() {
	if err := InintTrans("zh");err!=nil{
		fmt.Println("初始化获取翻译器错误")
		return
	}
	routes := gin.Default()
	routes.POST("/loginJSON",func(c *gin.Context) {
		var loginFor LoginForm
		if err:=c.ShouldBind(&loginFor);err!=nil{
			errs,ok:=err.(validator.ValidationErrors)
			if !ok {
				c.JSON(http.StatusOK,gin.H{
					"mgs":err.Error(),
				})
			}
			c.JSON(http.StatusOK,gin.H{
				"error":errs.Translate(trans),
			})
			fmt.Println(err.Error())
			c.JSON(http.StatusBadRequest,gin.H{
				"error":err.Error(),
			})
			return 
		}
		c.JSON(http.StatusOK,gin.H{
			"mag":"登录成功",
		})
	})
	routes.POST("/signup",func(c *gin.Context) {
		var SignUpForm SignUpForm
		if err := c.ShouldBind(&SignUpForm);err !=nil{
			fmt.Println(err.Error())
			c.JSON(http.StatusBadRequest,gin.H{
				"error":err.Error(),
			})
			return
		}
		c.JSON(http.StatusOK,gin.H{
			"msg":"登录成功",
		})
	})
	routes.Run(":8083")
}
启动
PS D:\golang\goproject\src\lnhgo> go run gin\ch07\main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] POST   /loginJSON                --> main.main.func1 (3 handlers)
[GIN-debug] POST   /signup                   --> main.main.func2 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8083
[GIN] 2026/08/31 - 19:18:00 | 200 |       0s |       127.0.0.1 | POST     "/loginJSON"
Key: 'LoginForm.User' Error:Field validation for 'User' failed on the 'min' tag
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 200 with 400
[GIN] 2026/08/31 - 19:18:17 | 200 |  11.53ms |       127.0.0.1 | POST     "/loginJSON"

  测试

image

 表单中文翻译的json格式化细节

package main

import (
	"fmt"
	"net/http"
	"reflect"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/locales/en"
	"github.com/go-playground/locales/zh"
	ut "github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
	enTranslations "github.com/go-playground/validator/v10/translations/en"
	zhTranslations "github.com/go-playground/validator/v10/translations/zh"
)
var trans ut.Translator
type LoginForm struct {
	User     string `json:"user"  binding:"required,min=3,max=10"` //前端传递的模式;各种类型数据
	Password string `json:"password" binding:"required"`
}
type SignUpForm struct{
	Age uint8 `json:"age" binding:"required,gte=1,lte=130"`//表示大于1;或者小于130
	Name string `json:"name" binding:"required,min=3"`
	Email string `json:"email" binding:"required,email"`
	Password string `json:"password" binding:"required"`
	RePassword string `json:"repassword" binding:"required,eqfield=Password"`//跨字段验证
}
func InintTrans(locale string)(err error){
	//修改gin框架中的validator引擎属性,实现定制
	if v,ok :=binding.Validator.Engine().(*validator.Validate);ok{
		//注册一个获取json的tag的自定义来                                                  
		v.RegisterTagNameFunc(func(fld reflect.StructField) string {
			name:= strings.SplitN(fld.Tag.Get("json"),",",2)[0]
			if name=="-"{
				return ""
			}
			return name
		})
		zhT:= zh.New()//中文
		enT:=en.New()//英文翻译
		//第一个参数备用语言环境,后续参数应该支持的语言环境
		uni:=ut.New(enT,zhT,enT)
		if trans,ok =uni.GetTranslator(locale);!ok{
			return fmt.Errorf("GetTranslator(%s)",locale)
		}else{
			switch locale{
			case "en":
				enTranslations.RegisterDefaultTranslations(v,trans)
			case "zh":
				zhTranslations.RegisterDefaultTranslations(v,trans)
			default:
				enTranslations.RegisterDefaultTranslations(v,trans)

			}
			return 
		}
		//return

	}
	return 
}

func main() {
	if err := InintTrans("zh");err!=nil{
		fmt.Println("初始化获取翻译器错误")
		return
	}
	routes := gin.Default()
	routes.POST("/loginJSON",func(c *gin.Context) {
		var loginFor LoginForm
		if err:=c.ShouldBind(&loginFor);err!=nil{
			errs,ok:=err.(validator.ValidationErrors)
			if !ok {
				c.JSON(http.StatusOK,gin.H{
					"mgs":err.Error(),
				})
			}
			c.JSON(http.StatusOK,gin.H{
				"error":errs.Translate(trans),
			})
			fmt.Println(err.Error())
			c.JSON(http.StatusBadRequest,gin.H{
				"error":err.Error(),
			})
			return 
		}
		c.JSON(http.StatusOK,gin.H{
			"mag":"登录成功",
		})
	})
	routes.POST("/signup",func(c *gin.Context) {
		var SignUpForm SignUpForm
		if err := c.ShouldBind(&SignUpForm);err !=nil{
			fmt.Println(err.Error())
			c.JSON(http.StatusBadRequest,gin.H{
				"error":err.Error(),
			})
			return
		}
		c.JSON(http.StatusOK,gin.H{
			"msg":"登录成功",
		})
	})
	routes.Run(":8083")
}
启动
PS D:\golang\goproject\src\lnhgo> go run gin\ch07\main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] POST   /loginJSON                --> main.main.func1 (3 handlers)
[GIN-debug] POST   /signup                   --> main.main.func2 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8083
Key: 'LoginForm.user' Error:Field validation for 'user' failed on the 'min' tag
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 200 with 400
[GIN] 2026/09/01 - 14:34:25 | 200 |    578µs |       127.0.0.1 | POST     "/loginJSON"
Key: 'LoginForm.user' Error:Field validation for 'user' failed on the 'min' tag
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 200 with 400
[GIN] 2026/09/01 - 14:35:35 | 200 |  788.1µs |       127.0.0.1 | POST     "/loginJSON"
[GIN] 2026/09/01 - 14:35:52 | 200 |       0s |       127.0.0.1 | POST     "/loginJSON"

  测试结果

image

 格式化

package main

import (
	"fmt"
	"net/http"
	"reflect"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/locales/en"
	"github.com/go-playground/locales/zh"
	ut "github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
	enTranslations "github.com/go-playground/validator/v10/translations/en"
	zhTranslations "github.com/go-playground/validator/v10/translations/zh"
)
var trans ut.Translator
type LoginForm struct {
	User     string `json:"user"  binding:"required,min=3,max=10"` //前端传递的模式;各种类型数据
	Password string `json:"password" binding:"required"`
}
type SignUpForm struct{
	Age uint8 `json:"age" binding:"required,gte=1,lte=130"`//表示大于1;或者小于130
	Name string `json:"name" binding:"required,min=3"`
	Email string `json:"email" binding:"required,email"`
	Password string `json:"password" binding:"required"`
	RePassword string `json:"repassword" binding:"required,eqfield=Password"`//跨字段验证
}
func removeTopStruct(fileds map[string]string) map[string]string{
	rsp :=map[string]string{}
	for field,err:=range fileds{
		rsp[field[strings.Index(field,".")+1:]]=err
	}
	return rsp
}
func InintTrans(locale string)(err error){
	//修改gin框架中的validator引擎属性,实现定制
	if v,ok :=binding.Validator.Engine().(*validator.Validate);ok{
		//注册一个获取json的tag的自定义来                                                  
		v.RegisterTagNameFunc(func(fld reflect.StructField) string {
			name:= strings.SplitN(fld.Tag.Get("json"),",",2)[0]
			if name=="-"{
				return ""
			}
			return name
		})
		zhT:= zh.New()//中文
		enT:=en.New()//英文翻译
		//第一个参数备用语言环境,后续参数应该支持的语言环境
		uni:=ut.New(enT,zhT,enT)
		if trans,ok =uni.GetTranslator(locale);!ok{
			return fmt.Errorf("GetTranslator(%s)",locale)
		}else{
			switch locale{
			case "en":
				enTranslations.RegisterDefaultTranslations(v,trans)
			case "zh":
				zhTranslations.RegisterDefaultTranslations(v,trans)
			default:
				enTranslations.RegisterDefaultTranslations(v,trans)

			}
			return 
		}
		//return

	}
	return 
}

func main() {
	if err := InintTrans("zh");err!=nil{
		fmt.Println("初始化获取翻译器错误")
		return
	}
	routes := gin.Default()
	routes.POST("/loginJSON",func(c *gin.Context) {
		var loginFor LoginForm
		if err:=c.ShouldBind(&loginFor);err!=nil{
			errs,ok:=err.(validator.ValidationErrors)
			if !ok {
				c.JSON(http.StatusOK,gin.H{
					"mgs":err.Error(),
				})
			}
			c.JSON(http.StatusOK,gin.H{
				"error":removeTopStruct(errs.Translate(trans)),
			})
			fmt.Println(err.Error())
			c.JSON(http.StatusBadRequest,gin.H{
				"error":err.Error(),
			})
			return 
		}
		c.JSON(http.StatusOK,gin.H{
			"mag":"登录成功",
		})
	})
	routes.POST("/signup",func(c *gin.Context) {
		var SignUpForm SignUpForm
		if err := c.ShouldBind(&SignUpForm);err !=nil{
			fmt.Println(err.Error())
			c.JSON(http.StatusBadRequest,gin.H{
				"error":err.Error(),
			})
			return
		}
		c.JSON(http.StatusOK,gin.H{
			"msg":"登录成功",
		})
	})
	routes.Run(":8083")
}
启动
PS D:\golang\goproject\src\lnhgo> go run gin\ch07\main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] POST   /loginJSON                --> main.main.func1 (3 handlers)
[GIN-debug] POST   /signup                   --> main.main.func2 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8083
Key: 'LoginForm.user' Error:Field validation for 'user' failed on the 'min' tag
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 200 with 400
[GIN] 2026/09/01 - 15:20:37 | 200 |    1.2ms |       127.0.0.1 | POST     "/loginJSON"
[GIN] 2026/09/01 - 15:20:55 | 200 |  392.8µs |       127.0.0.1 | POST     "/loginJSON"
Key: 'LoginForm.user' Error:Field validation for 'user' failed on the 'min' tag
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 200 with 400
[GIN] 2026/09/01 - 15:21:10 | 200 |  682.1µs |       127.0.0.1 | POST     "/loginJSON"
invalid character '}' looking for beginning of value
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 200 with 400
[GIN] 2026/09/01 - 15:21:18 | 200 |       0s |       127.0.0.1 | POST     "/loginJSON"
Key: 'LoginForm.user' Error:Field validation for 'user' failed on the 'min' tag
Key: 'LoginForm.password' Error:Field validation for 'password' failed on the 'required' tag
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 200 with 400
[GIN] 2026/09/01 - 15:21:29 | 200 |       0s |       127.0.0.1 | POST     "/loginJSON"

  测试

image

gin 中间件

c.Next() 的意义不是“让后续流程自动执行”,而是让当前中间件“主动进入后续 Handler”,然后再回来继续执行当前中间件后面的代码。它就像一个分界线

package main

import (
	"fmt"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
)
func MyLogger() gin.HandlerFunc{
	return  func(c *gin.Context) {
		t:=time.Now()
		c.Set("example","123456")
		
		c.Next()//让原来该执行的逻辑继续执行
		end:=time.Since(t)
		fmt.Printf("耗时%v\n",end)
		status :=c.Writer.Status()
		fmt.Println("状态",status)
	}
}
func main() {
	router := gin.Default()
	router.Use(MyLogger())
	router.GET("/ping",func(c *gin.Context) {
		c.JSON(http.StatusOK,gin.H{
			"message":"pong",
		})
	})
	router.Run(":8083")
}
启动
PS D:\golang\goproject\src\lnhgo> go run gin\ch08\main.go 
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> main.main.func1 (4 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8083
耗时0s
状态 200
[GIN] 2026/09/01 - 19:00:12 | 200 |       0s |       127.0.0.1 | GET      "/ping"
耗时0s
状态 404
[GIN] 2026/09/01 - 19:00:12 | 404 |       0s |       127.0.0.1 | GET      "/favicon.ico"

  中间件终止后续流程abort

package main

import (
	"fmt"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
)
func MyLogger() gin.HandlerFunc{
	return  func(c *gin.Context) {
		t:=time.Now()
		c.Set("example","123456")
		//return 
		c.Next()//让原来该执行的逻辑继续执行
		end:=time.Since(t)
		fmt.Printf("耗时%v\n",end)
		status :=c.Writer.Status()
		fmt.Println("状态",status)
	}
}
func TokenRequired() gin.HandlerFunc{
	return  func(c *gin.Context) {
		var token string
		for k,v:=range c.Request.Header{
			//if k == "x-token"{注意这里一定要首字母大写
			if k=="X-Token"{
				token=v[0]
			}else{
				fmt.Println(k,v)
			}
		}
		if token != "bobby"{
			c.JSON(http.StatusUnauthorized,gin.H{
				"msg":"未登录",
			})
			//return在这里无法终止后续流程
			c.Abort()//必须如此才能终止后续流程 
		}
		c.Next()
	}
}
func main() {
	router := gin.Default()
	router.Use(TokenRequired())//框架调用
	router.GET("/ping",func(c *gin.Context) {
		c.JSON(http.StatusOK,gin.H{
			"message":"pong",
		})
	})
	router.Run(":8083")
}
启动
PS D:\golang\goproject\src\lnhgo> go run gin\ch08\main.go 
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> main.main.func1 (4 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8083

  postman 测试中间件阶段终止

image

postman 测试完整中间件通过流程

image

 gin 中间件原理

可以把 c.Next() 理解成 Gin 中间件里的一个**“分界线 / 控制点”**。

你的描述稍微精确一下就是:

func Middleware() gin.HandlerFunc {
    return func(c *gin.Context) {

        // ① Next 上面的代码
        fmt.Println("A")

        c.Next()

        // ② Next 下面的代码
        fmt.Println("B")
    }
}

  

假设后面还有:

func Handler(c *gin.Context) {
    fmt.Println("C")
}

  

那么执行顺序就是:

进入 Middleware
       ↓
执行 A
       ↓
遇到 c.Next()
       ↓
进入 Gin 后面的 Handler Chain
       ↓
执行 C
       ↓
后面的 Handler Chain 执行完
       ↓
返回 Middleware
       ↓
继续执行 c.Next() 后面的 B
       ↓
Middleware 结束

  

最终:

A
C
B

 所以可以把它记成:

┌──────────── Middleware ────────────┐
│                                    │
│        Next() 前面的代码            │
│               ↓                    │
│            c.Next()                │ ← 分界线
│               ↓                    │
│        后续 Gin Handler              │
│               ↓                    │
│          执行完返回                  │
│               ↓                    │
│        Next() 后面的代码             │
│                                    │
└────────────────────────────────────┘

  

 

而且这里有一个特别重要的理解

c.Next() 不是“调用下一个函数然后就不回来了”,而是“进入后续 Handler Chain,等后续 Chain 执行完成,再回来继续当前中间件”。

所以以后看到这种代码:

start := time.Now()

c.Next()

cost := time.Since(start)

  

你马上就应该想到:

记录开始时间
    ↓
进入 Controller
    ↓
Controller 执行完
    ↓
回来
    ↓
计算耗时

  

这也是为什么日志、耗时统计、权限、事务、recover 等很多功能都特别适合用 Gin 中间件实现。

你现在c.Next() 的理解已经到位了。

 

为什么return在这里会出现无法终止后续流程

因为 Gin 中间件里的 return 只能终止“当前这个中间件函数”,不能自动终止整个 Gin 的 c.Next() 执行链。

这是 Gin 中间件最容易搞混的地方。

先看一个典型例子

func MyMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {

        fmt.Println("中间件开始")

        if true {
            return
        }

        fmt.Println("中间件结束")
    }
}

  

这里 return 确实会执行:

中间件开始
↓
return
↓
当前中间件函数结束

  

但是如果你的代码结构是:

func MyMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {

        if true {
            return
        }

        c.Next()
    }
}

  

return 后面的 c.Next() 不会执行。

但这不代表整个请求链一定停止。


关键在于:Gin 的中间件是怎么调用的

假设:

r.Use(Middleware1())

r.GET("/test", func(c *gin.Context) {
    fmt.Println("业务处理")
})

Gin 内部可以简单理解成:

Middleware1
    ↓
Middleware2
    ↓
Handler

c.Next() 的作用就是:

继续执行当前中间件后面的 Handler 链。

例如:

func Middleware1() gin.HandlerFunc {
    return func(c *gin.Context) {

        fmt.Println("M1 前")

        c.Next()

        fmt.Println("M1 后")
    }
}

执行:

M1 前
  ↓
c.Next()
  ↓
后面的 Middleware / Handler
  ↓
M1 后

为什么 return 经常让人觉得“没终止”?

因为你可能写的是这种:

func AuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {

        if token == "" {
            c.JSON(401, gin.H{
                "message": "未登录",
            })
            return
        }

        c.Next()
    }
}

这个实际上是可以阻止后续 Handler 的。

执行:

AuthMiddleware
      ↓
token == ""
      ↓
c.JSON(...)
      ↓
return
      ↓
AuthMiddleware 结束

  

因为 c.Next() 根本没有执行

所以:

如果你是在中间件里直接 return,并且没有提前调用 c.Next(),正常情况下后续 Handler 不会执行。


但是有一种情况非常容易踩坑

比如:

func Middleware() gin.HandlerFunc {
    return func(c *gin.Context) {

        c.Next()

        if somethingWrong {
            return
        }
    }
}

这里:

c.Next()

已经把后面的 Handler 执行完了。

所以:

c.Next()

return

相当于:

先执行后续所有 Handler
        ↓
回来
        ↓
return

这时候你再 return,当然阻止不了后面的执行。


真正终止 Gin 后续链的方法

如果你想表达:

“从这里开始,后面的 Handler 一个都不要执行。”

最常见的是:

c.Abort()

例如:

func AuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {

        if token == "" {

            c.JSON(401, gin.H{
                "message": "未登录",
            })

            c.Abort()
            return
        }

        c.Next()
    }
}

  

这里:

c.Abort()
return

两个东西作用不同:

c.Abort()

告诉 Gin:

这个请求的 Handler Chain 到此为止。

return

告诉 Go:

当前这个函数到此为止。

所以通常一起写:

c.Abort()
return

非常清晰。


你可以把它记成一句话

return 是退出函数,c.Abort() 是终止 Gin 的执行链。

操作作用
return 退出当前 Go 函数
c.Next() 继续执行后面的 Handler
c.Abort() 终止 Gin 后续 Handler 链
c.Abort(); return 终止 Gin 链 + 退出当前中间件

最重要的一个理解

如果你看到:

func Middleware() gin.HandlerFunc {
    return func(c *gin.Context) {

        if !check() {
            c.JSON(403, gin.H{
                "error": "forbidden",
            })
            c.Abort()
            return
        }

        c.Next()
    }
}

  

你应该脑子里形成这个模型:

请求
 ↓
Middleware
 ↓
检查
 ├── 失败 → c.Abort() → return → 结束
 │
 └── 成功 → c.Next()
              ↓
           下一个中间件
              ↓
           Controller

  

另外一个很关键的点:c.Abort() 本身也不会让 Go 函数立即退出。
所以很多 Gin 代码才会写成:

c.Abort()
return

  

这两个不是重复,而是分别控制 Gin 的执行链Go 当前函数

gin 是函数链

这个理解方向是对的,但“队列”这个词可以再精确一点。

Gin 更准确地说是一个 Handler 链(Handler Chain),可以把它理解成一个按顺序排列的函数链:

Middleware1 → Middleware2 → Middleware3 → Handler

你的 return

return

只会让当前正在执行的匿名函数退出

Middleware1
    ↓
return
    ↓
Middleware1 结束
    ↓
Gin 的外层执行机制
    ↓
后续 Handler 仍可能继续

  

所以你说的:

“return 的只是当前返回的匿名函数,不影响下一个函数执行”

  

这个核心理解是对的。

但有一个非常重要的补充:

c.Next() 是“推进 Handler 链”的关键

可以简单理解成:

┌──────── Middleware ────────┐
│                            │
│  前置逻辑                   │
│      ↓                     │
│   c.Next() ─────────────┐  │
│                         ↓  │
└────────────────────────────┘
                          ↓
                    后续 Handler
                          ↓
                    执行完成
                          ↓
                    返回当前中间件
                          ↓
                      后置逻辑

  

而:

c.Abort()

相当于告诉 Gin:

Handler Chain
    ↓
❌ 后面的不要再执行

  

所以你可以最终记成一句非常核心的话:

return 控制的是 Go 当前函数;c.Next() 控制的是进入后续 Handler;c.Abort() 控制的是 Gin Handler 链是否继续。

  

这三个概念分清楚,Gin 中间件的原理就基本吃透了。

gin 模版返回html

 

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	router := gin.Default()
	//LoadHTMLFiles 将指定文件目录加载好,但是是执行目录的相对目录
	router.LoadHTMLFiles("gin/ch09/templates/index.tmpl")
	router.GET("/index",func(c *gin.Context) {
		c.HTML(http.StatusOK,"index.tmpl",gin.H{
			"title":"慕课网",
		})
	})
	router.Run(":8082")
}

index.tmpl 文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>   
    {{ .title }}
    </h1>
</body>
</html>



启动
PS D:\golang\goproject\src\lnhgo> go run gin\ch09\main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /index                    --> main.main.func1 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8082
[GIN] 2026/09/01 - 21:20:28 | 200 |  692.4µs |       127.0.0.1 | GET      "/index"

 浏览器访问http://127.0.0.1:8082/index

image

加载多个html 文件

 

  测试2

image

加载多级目录

 

package main

import (
	//"fmt"
	"net/http"
	//"os"
	//"path/filepath"

	"github.com/gin-gonic/gin"
)

func main() {
	router := gin.Default()
	//dir,_ := filepath.Abs(filepath.Dir(os.Args[0]))
	//fmt.Println(dir)
	//LoadHTMLFiles 将指定文件目录加载好,但是目录是相对目录
	//router.LoadHTMLGlob("gin/ch09/templates/*")//加载指定目录下所有文件
	router.LoadHTMLGlob("gin/ch09/templates/**/*")//加载所有二级目录下的所有文件
	//router.LoadHTMLFiles("gin/ch09/templates/index.tmpl","gin/ch09/templates/goods.html")//加载指定的两个文件
	router.GET("/index",func(c *gin.Context) {
		c.HTML(http.StatusOK,"index.tmpl",gin.H{
			"title":"慕课网",
		})
	})
	router.GET("/goods/list",func(c *gin.Context) {
		c.HTML(http.StatusOK,"goods/list.html",gin.H{ // goods/list.html 在html文件里填写
			"title":"慕课网",
		})
	})
	router.GET("/user/list",func(c *gin.Context) {
		c.HTML(http.StatusOK,"user/list.html",gin.H{//user/list.html 在html 文件里填写
			"title":"慕课网",
		})
	})
	router.GET("/goods",func(c *gin.Context) {
		c.HTML(http.StatusOK,"goods.html",gin.H{
			"name":"bobby",
		})
	})
	router.Run(":8082")
} 
启动
PS D:\golang\goproject\src\lnhgo> go run gin\ch09\main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] Loaded HTML Templates (4): 
        - 
        - list.html
        - goods/list.html
        - user/list.html

[GIN-debug] GET    /index                    --> main.main.func1 (3 handlers)
[GIN-debug] GET    /goods/list               --> main.main.func2 (3 handlers)
[GIN-debug] GET    /user/list                --> main.main.func3 (3 handlers)
[GIN-debug] GET    /goods                    --> main.main.func4 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8082
[GIN] 2026/09/02 - 13:32:10 | 200 |   1.85ms |       127.0.0.1 | GET      "/goods/list"
[GIN] 2026/09/02 - 13:32:12 | 200 |  783.9µs |       127.0.0.1 | GET      "/goods/list"
[GIN] 2026/09/02 - 13:32:35 | 200 |   1.05ms |       127.0.0.1 | GET      "/user/list"

  

   html 页面

{{define "goods/list.html"}}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>商品列表页</h1>
</body>
</html>
{{end}}

  user/list.html

{{define "user/list.html"}}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head> 
<body>
    <h1>   
    用户列表页
    </h1>
</body>
</html>
{{end}}

  

  

目录结构

image

 浏览器访问http://127.0.0.1:8082/goods/list

image

 浏览器访问http://127.0.0.1:8082/user/list

image

 

如何有优雅退出程序

 

package main

import (
	"fmt"
	"net/http"
	"os"
	"os/signal"

	//"sync"
	"syscall"

	"github.com/gin-gonic/gin"
)

func main() {
	routes := gin.Default()
	routes.GET("/",func(c *gin.Context) {
		c.JSON(http.StatusOK,gin.H{
			"msg":"pong",
		})
	})
	go func ()  {
		routes.Run(":8083")	
	}()
	//如果想要接收kill-9强杀命令;不会处理后续逻辑的
	quit := make(chan os.Signal,1)
	signal.Notify(quit,syscall.SIGINT,syscall.SIGTERM)
	<-quit
	fmt.Println("关闭中")
	fmt.Println("注销操作")

}
启动测试
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /                         --> main.main.func1 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8083
关闭中
注销操作
posted @ 2026-09-02 14:28  烟雨楼台,行云流水  阅读(6)  评论(0)    收藏  举报