【笔记】Gin学习笔记

前言

Gin 是一个用 Go (Golang) 编写的 Web 框架。 它具有类似 martini 的 API,性能要好得多,多亏了 httprouter,速度提高了 40 倍。 如果您需要性能和良好的生产力,您一定会喜欢 Gin。(官方

下载依赖

1
go get github.com/gin-gonic/gin

引入模块

1
import "github.com/gin-gonic/gin"

获取Engine对象

1
engine := gin.New()
  • 通过gin.Default()创建的实例会自带Logger中间件和Recovery中间件

Recovery中间件的作用是:如果程序执行过程中遇到panic中断了服务,则Recovery会恢复程序执行,并返回服务器500内部错 误。

1
engine := gin.Default()

处理HTTP请求

通用的请求处理

处理GET请求

default:如果没有请求,为变量赋值默认值

1
2
3
4
5
6
7
8
9
10
11
// 创建引擎对象
engine := gin.Default()
// 创建控制器
engine.Handle("GET", "/", func(context *gin.Context) {
// 获取GET请求参数
value := context.DefaultQuery("key", "default")
// 返回响应数据
context.Writer.Write([]byte(value))
})
// 开启服务器
engine.Run()

处理POST请求

1
2
3
4
5
6
7
8
9
10
11
// 创建引擎对象
engine := gin.Default()
// 创建控制器
engine.Handle("POST", "/", func(context *gin.Context) {
// 获取POST请求参数
value := context.PostForm("key")
// 返回响应数据
context.Writer.Write([]byte(value))
})
// 开启服务器
engine.Run()

处理RESTful的DELETE请求

1
2
3
4
5
6
7
8
9
10
11
// 创建引擎对象
engine := gin.Default()
// 创建控制器
engine.Handle("DELETE", "/:key", func(context *gin.Context) {
// 获取POST请求参数
value := context.Param("key")
// 返回响应数据
context.Writer.Write([]byte(value))
})
// 开启服务器
engine.Run()

请求

获取请求行中的数据

获取请求方式

1
context.Request.Method

获取请求资源路径

1
path := context.FullPath();

获取请求者的IP地址

1
context.ClientIP()

获取请求头中的数据

获取指定请求头的数据

<key>:请求头的键

1
context.Request.Header.Get("<key>")

获取请求携带的参数

获取GET请求的请求参数

1
value := context.Query("key")
指定参数的默认值

default:如果没有请求,为变量赋值默认值

1
value := context.DefaultQuery("key", "default")

获取POST请求的请求参数

1
value := context.PostForm("key")
参数是否存在

exist:如果存在指定参数,则返回true;如果不存在指定参数,则返回false

1
value, exist := context.GetPostForm("key")

获取RESTful的请求参数

  • 在传递参数时,通过:标记占位符

http://example.com/:key

1
value := context.Param("key")

表单参数实体绑定

传递query参数

request
1
GET http://localhost:8080/api?username=1&password=1
1
2
3
4
type User struct {
Username string `form:"username"`
Password string `form:"password"`
}
1
2
3
4
5
var user User
err := context.ShouldBindQuery(&user)
if err != nil {
log.Fatal(err.Error())
}

传递JSON参数

request
1
2
3
4
POST http://localhost:8080/api
Content-Type: application/json

{"username": 1, "password": 1}
1
2
3
4
type User struct {
Username string `json:"username"`
Password string `json:"password"`
}
1
2
3
4
5
var user User
err := context.ShouldBindJSON(&user)
if err != nil {
...
}

传递uri参数

request
1
GET http://localhost:8080/api/:username/:password
1
2
3
4
type User struct {
Username string `uri:"username"`
Password string `uri:"password"`
}
1
2
3
4
5
var user User
err := context.ShouldBindUri(&user)
if err != nil {
...
}

自动映射结构体

  • 根据请求头中的Content-Type自动映射结构体
1
2
3
4
type User struct {
Username string `form:"username" json:"username"`
Password string `form:"password" json:"password"`
}
1
2
3
4
5
var user User
err := context.ShouldBindJSON(&user)
if err != nil {
...
}

表单参数校验

自定义校验函数

required:必填项校验

1
2
3
4
5
6
7
8
9
10
11
type User struct {
Username string `form:"username" json:"username" uri:"username" binding:"required"`
Password string `form:"password" json:"password" uri:"password" binding:"required,validateFunction"`
}

func validateFunction(fieldLevel validator.FieldLevel) bool {
if (fieldLevel.field().Interface().(string) == "1") {
return false
}
return true
}

响应

获取响应行的数据

获取响应状态码

1
context.Writer.Status()

设置响应头的数据

设置指定请求头的数据

<key>:响应头键
<key>:响应头值

1
context.Writer.Header().Set("<key>", "<value>")
1
context.Header("<key>", "<value>")
1
context.Set("<key>", "<value>")

设置响应数据

返回字节数组

1
context.Writer.Writer([]byte(""))

返回字符串

1
context.Writer.WriterString("")

返回JSON格式字符串

Map转换为JSON

http.StatusOK:200

1
2
3
4
5
context.JSON(http.StatusOK, map[string]interface{}{
"code": 1,
"msg": "ok",
"data": 数据,
})
结构体转换为JSON

http.StatusOK:200

1
2
3
4
5
type Response struct {
Code int
Msg string
Date interface{}
}
1
2
3
4
5
6
resp := Response{
code: 1,
message: "ok",
data: 数据
}
context.JSON(http.StatusOK, &resp)

返回HTML页面

传送门

路由组

1
2
3
4
5
6
7
8
9
10
11
12
// 创建引擎对象
engine := gin.Default()
// 创建路由组
routerGroup := engine.Group("/user")
routerGroup.POST("/login", func(context *gin.Context) {
context.Writer.WriteString("")
})
routerGroup.POST("/register", func(context *gin.Context) {
context.Writer.WriteString("")
})
// 开启服务器
engine.Run()

抽离路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func main() {
// 创建引擎对象
engine := gin.Default()
// 创建路由组
routerGroup := engine.Group("/user")
routerGroup.POST("/register", register)
routerGroup.POST("/login", login)
// 开启服务器
engine.Run()

}

func login(context *gin.Context) {
context.Writer.WriteString("")
}
func register(context *gin.Context) {
context.Writer.WriteString("")
}

中间件

举例:定义一个中间件,用于打印请求方式与请求路径

定义中间件函数

  • 中间件函数返回HandleFunc类型

context.Next():遇到Next函数立即跳到下一个中间件或请求处理函数
context.Abort():遇到Abort函数立即终止后面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func 中间件函数名() gin.HandlerFunc {
return func(context *gin.Context) {

// 处理业务之前的代码
if 布尔表达式 {
context.Abort()
}

context.Next()

// 处理业务之后的代码

}
}

挂载中间件

为全局挂载中间件

printInfo():中间件函数

1
engine.Use(中间件函数名())
按顺序挂载多个中间件
1
engine.Use(中间件函数名(), 中间件函数名())
1
engine.Use(中间件函数名()).Use(中间件函数名())

为指定处理器挂载中间件

printInfo():中间件函数

1
2
3
engine.Handle("GET", "/", 中间件函数名(), func(context *gin.Context) {
...
})

为路由组挂载中间件

  • 被挂载的中间件只在指定的路由组中生效
1
2
3
4
5
var routerGroup = engine.Group("/")
routerGroup.Use(中间件函数名())
routerGroup.GET("", func(context *gin.Context) {
...
})

文件上传

  • 接收前端上传的文件

接收单文件

1
2
file, err := context.FormFile("key")
err := context.SaveUploadedFile(file, "./"+file.Filename)

接收多文件

1
2
3
4
5
form, err := context.MultipartForm()
fileList, err := form.File["key_1", "key_2"]
for _, file := range fileList {
...
}

日志

输出位置

仅输出到控制台(缺省值)

1
gin.DefaultWriter = io.MultiWriter(os.Stdout)

仅输出到文件

1
2
f, err := os.Create("gin.log")
gin.DefaultWriter = io.MultiWriter(f)

同时输出到控制台和文件

1
2
f, err := os.Create("gin.log")
gin.DefaultWriter = io.MultiWriter(os.Stdout, f)

自定义日志格式

1
2
3
4
5
6
7
8
9
10
11
12
13
router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
return fmt.Sprintf("%s %s %s %s %s %d %s %s %s\n",
param.ClientIP,
param.TimeStamp.Format(time.RFC1123),
param.Method,
param.Path,
param.Request.Proto,
param.StatusCode,
param.Latency,
param.Request.UserAgent(),
param.ErrorMessage,
)
}))

完成

参考文献

哔哩哔哩——80技术
完美代码
哔哩哔哩——筱筱知晓
哔哩哔哩——go圈里最会写js的奇淼
哔哩哔哩——go圈里最会写js的奇淼
Gin中文文档