环己三烯的冬眠舱

天天网抑云,偶尔读点书。

0%

Gin学习笔记

Gin是基于Go开发的Web微框架,相当简洁好用。

Download Gin

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

Usage

需要 import "github.com/gin-gonic/gin"

创建

  • gin.Default()

生成一个gin实例。

接收请求

HTTP请求中有GET,POST,PUT,PATCH,DELETEOPTIONS等方法,还有一个Any可以匹配所有方法。

  • GET(path, func)

声明一个“路由”(即被请求的路径path),当客户端(浏览器)使用HTTP的GET方法向服务器请求位于path的页面时,触发func所定义的函数进行处理。这个func只有一个参数,即gin.Context类型的指针,这个指针指向的地址空间储存了一些对传来的HTTP报文解析后的信息,解析过程是gin封装好的。POST(path, func) 等也同理。

此外,Gin还支持分组路由功能。例如有一组路由均为/a打头,则可以写为:

1
2
3
4
5
a := r.Group("/a")
{
v1.GET("/b", defaultHandler) // GET /a/b
v1.GET("/c", defaultHandler) // GET /a/c
}

数据收集

有时请求的URL中会承载一定的信息,这部分信息可以使用Param()Query()获取。

  • Param(string)

如果要使用Param()方法,则声明的URL中需要包含对应的“占位符”(不清楚学名,笔者自己这么称呼的),用冒号来表示。例如:

1
2
3
4
r.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
c.String(http.StatusOK, "hello, %s", name)
})

此时,如果浏览器向服务器请求"/user/dzc",则服务端会返回字符串"hello, dzc"

  • Query(string)

如果要使用Query()方法,则实际请求的URL中需要包含问号?,将需要发送到服务端的信息以键值对的形式放在问号后面,键值对之间使用&分隔。例如:

1
2
3
4
5
6
r.GET("/welcome", func(c *gin.Context) {
first := c.Query("first")
last := c.Query("last")
out := "hello, " + first + last
c.String(http.StatusOK, out)
})

此时,如果浏览器向服务器请求"/welcome?first=d&last=zc",则服务器回返回字符串hello, dzc

另一些时候,浏览器的数据会以POST方法发送给服务器,这部分数据可以使用PostForm()来解析。

  • PostForm(string) & DefaultPostForm(string, string)

用POST方法提交的表单数据也是以键值对的形式表示的,可以使用PostForm()将其解析出来,而该方法的另一个变种DefaultPostForm()则允许使用者在未解析到所需字段时设置一个默认结果,例如:

1
2
3
4
5
6
7
8
9
r.POST("/register", func(c *gin.Context) {
login := c.PostForm("login")
passwd := c.DefaultPostForm("pass","defaultpasswd")
if login == "3200104203" && passwd == "4203" {
c.String(http.StatusOK, "Login successful.")
} else {
c.String(http.StatusOK, "Wrong password.")
}
})

返回数据

Gin支持返回多种类型的数据,如字符串、JSON、HTML页面等。

  • String(int, string)

String()用于返回一个字符串,第一个参数是返回的HTTP状态码,第二个参数是要返回的字符串内容。例如:c.String(http.StatusOK, "hello world")

  • JSON(int, gin.H)

JSON()用于返回一个JSON对象,第一个参数是返回的HTTP状态码,第二个参数是要返回的JSON内容。gin.H类型实际上就是map[string]interface{},其中空接口可以代表任何类型(所有类型均视为实现了一个空接口),所以gin.H类的实例相当于一个JSON对象。

  • HTML(int, string, gin.H)

HTML()用于返回一个HTML页面,第一个参数是返回的HYTTP状态码,第二个参数是要返回的页面的路径,第三个参数是需要嵌入到HTML中的数据(如果HTML中出现{{.msg}},即Gin的模板语法,则会用"this is a message from server."代替)。在使用这个函数之前,需要用LoadHTMLGlob()LoadHTMLFiles()将HTML页面文件从硬盘加载进内存中,其中前者可以一次加载整个目录下的所有文件,而后者则单独加载某一个文件。例如:

1
2
3
4
5
6
r.LoadHTMLFiles("root/index.html") // 或 r.LoadHTMLGlob("root/*")
r.GET("/reg", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", gin.H{
"msg": "this is a message from server.",
})
})

中间件

中间件,顾名思义就是在浏览器和服务器中间的一层东西。而这层东西可以对浏览器发来的请求进行拦截并进行一些预处理,例如权限验证等。此外,中间件还可以在服务器完成处理后、向客户端发送响应前进行一些处理(如添加统一的响应头等)。Gin有内置一些中间件,如默认使用的Logger()Recovery()

  • 全局使用中间件Use(func)

Use()方法用于全局使用中间件。例如:r.Use(gin.Recovery())

  • 路由分组使用中间件

在创建路由分组时可以添加该路由分组使用的中间件。

例如:user := router.Group("user",gin.Recovery())

  • 单个路由使用中间件

在创建单个路由时也可以添加该路由使用的中间件。

例如:r.GET("/",gin.Recovery(),DefaultHandler)

  • 自定义中间件

Gin规范了自定义中间件的方式:

1
2
3
func MyMiddleware(c *gin.Context){

}

在自定义中间件时,如果需要与服务端进行数据传递的话,可以使用Set()Get()方法。

  • Set(string, interface{})

使用Set()时,相当于为gin.Context设置了一个键值对。其中键必须是string类型的,而值可以是任意类型的。

  • Get(string)

使用Get()可以将之前Set()的值读取出来,其返回两个值,第一个是键对应的值,第二个是该键是否存在,用布尔类型表示。

Next()方法可以划分中间件的前置和后置功能。Next()调用前的代码将在请求到达服务端之前进行,而Next()调用后的代码则会在服务端处理完毕后、正式向客户端发送响应前运行。

Abort()方法可以拦截请求/响应。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
MyMiddlewareForAuth := func(c *gin.Context) {
login := c.PostForm("login")
passwd := c.PostForm("pass")
if login == "3200104203" && passwd == "4203" {
c.Set("Authres", 1)
} else {
c.Set("Authres", 0)
c.Next()
c.String(http.StatusOK, "Wrong password.")
}
}

r.POST("/register", MyMiddlewareForAuth, func(c *gin.Context) {
if val, exists := c.Get("Authres"); exists && val == 1 {
c.String(http.StatusOK, "Login successful.")
} else {
c.String(http.StatusOK, "hello?\n")
}
})

Reference

https://geektutu.com/post/quick-go-gin.html

https://zhuanlan.zhihu.com/p/151818857

https://cloud.tencent.com/developer/article/1585029

有的没的

没事听点歌(Billie Eilish - bad guy)