gin使用笔记
# gin使用笔记
# 1、什么是gin
Gin 是一个用 Go (Golang) 编写的高性能、易用的 Web 框架。它的设计目标是简洁、高效,并提供快速的开发体验
# 2、安装
go get -u github.com/gin-gonic/gin
1
# 3、Quickstart
func TestQuickStart(t *testing.T) {
// 创建一个默认的路由引擎
r := gin.Default()
// GET请求,客户端访问'/ping'时,返回'pong'
r.GET("/ping", func(ctx *gin.Context) {
//ctx.JSON返回json数据
// gin.H 是map[string]interface{}的缩写
ctx.JSON(200, gin.H{
"message": "pong",
})
})
// 启动HTTP服务,默认再0.0.0.0:8080启动服务
// r.Run()
// 指定端口
r.Run(":8085")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 4、Restful API
RESTful API 是一种基于 REST(Representational State Transfer)架构风格的应用程序编程接口。REST 是一种设计和开发网络应用的架构风格,同样的一个url可以搭配上不同的 HTTP 动词来表示操作。这些动词包括:
GET
:读取资源POST
:创建资源PUT
:更新资源DELETE
:删除资源 以上的HTTP动词gin都支持:
func TestRestful(t *testing.T) {
r := gin.Default()
// GET
r.GET("/ping", func(ctx *gin.Context) {
ctx.JSON(200, gin.H{
"message": "get",
})
})
// POST
r.POST("/ping", func(ctx *gin.Context) {
ctx.JSON(200, gin.H{
"message": "post",
})
})
// PUT
r.PUT("/ping", func(ctx *gin.Context) {
ctx.JSON(200, gin.H{
"message": "put",
})
})
// DELETE
r.DELETE("/ping", func(ctx *gin.Context) {
ctx.JSON(200, gin.H{
"message": "del",
})
})
r.Run(":8085")
}
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
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
TODO
遵循rest风格的好处坏处
# 5、参数获取
# 5.1、query 参数
query
是指附加再URL末尾的键值对,以(?
)开始,每个参数以键值对的形式出现,键和值之间用等号(=)分隔,不同的参数之间用与号(&)分隔
http://example.com/search?q=golang&sort=asc&page=2
1
在这个 URL 中,q=golang
、sort=asc
和 page=2
都是查询参数。q
、sort
和 page
是参数的键,对应的 golang
、asc
和 2
是这些键的值。
func TestQueryParam(t *testing.T) {
r := gin.Default()
// Query:如果参数不存在则返回空字符串
r.GET("/search/query", func(c *gin.Context) {
query := c.Query("query")
c.JSON(200, gin.H{
"query": query,
})
})
// DefaultQuery:允许指定一个默认值,如果参数不存在则返回默认值
r.GET("/search/default/query", func(c *gin.Context) {
query := c.DefaultQuery("query", "default")
c.JSON(200, gin.H{
"query": query,
})
})
// GetQuery:除了参数值,多返回一个布尔值,指示参数是否存在
r.GET("/search/get/query", func(c *gin.Context) {
query, exists := c.GetQuery("query")
if !exists {
query = "default"
}
c.JSON(200, gin.H{
"query": query,
})
})
// QueryMap:获取多个键值对
// http://xxx?filters[name]=John&filters[age]=30
r.GET("/search/query/map", func(c *gin.Context) {
filters := c.QueryMap("filters")
c.JSON(200, gin.H{
"filters": filters,
})
})
// QueryArray 返回多个相同键的值
// http://xxx?tag=go&tag=gin
r.GET("/search/query/array", func(c *gin.Context) {
tags := c.QueryArray("tag")
c.JSON(200, gin.H{
"tags": tags,
})
})
// 获取所有参数
r.GET("/search/all", func(c *gin.Context) {
// 获取所有查询参数
queryParams := c.Request.URL.Query()
// 将查询参数转换为 map[string]string
params := make(map[string]string)
for key, values := range queryParams {
if len(values) > 0 {
params[key] = values[0]
}
}
c.JSON(200, params)
})
r.Run(":8085")
}
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
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
PS:
- filters:
{
"filters": {
"age": "30",
"name": "John"
}
}
1
2
3
4
5
6
2
3
4
5
6
- tags:
{
"tags": ["go", "gin"]
}
1
2
3
2
3
# 5.2、form 参数
PostForm:获取表单参数
func TestFormParam(t *testing.T) {
r := gin.Default()
// PostForm:获取表单参数
r.POST("/form", func(c *gin.Context) {
name := c.PostForm("name")
age := c.PostForm("age")
c.JSON(200, gin.H{
"name": name,
"age": age,
})
})
// DefaultPostForm:允许指定一个默认值,如果参数不存在则返回默认值
r.POST("/form/default", func(c *gin.Context) {
name := c.DefaultPostForm("name", "anonymous")
age := c.DefaultPostForm("age", "0")
c.JSON(200, gin.H{
"name": name,
"age": age,
})
})
// GetPostForm:除了参数值,多返回一个布尔值,指示参数是否存在
r.POST("/form/get", func(c *gin.Context) {
name, nameExists := c.GetPostForm("name")
age, ageExists := c.GetPostForm("age")
if !nameExists {
name = "anonymous"
}
if !ageExists {
age = "0"
}
c.JSON(200, gin.H{
"name": name,
"age": age,
})
})
// PostFormMap:获取多个键值对
// http://xxx?filters[name]=John&filters[age]=30
r.POST("/form/map", func(c *gin.Context) {
user := c.PostFormMap("user")
c.JSON(200, gin.H{
"user": user,
})
})
// PostFormArray 返回多个相同键的值
// http://xxx?tag=go&tag=gin
r.POST("/form", func(c *gin.Context) {
tags := c.PostFormArray("tag")
c.JSON(200, gin.H{
"tags": tags,
})
})
r.Run(":8085")
}
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
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
# 5.4、Json参数
PS:一般都是用绑定BindJSON
(下面会提到)去获取,但也提供了GetRawData
方式
func TestJsonParam(t *testing.T) {
r := gin.Default()
r.POST("/json", func(c *gin.Context) {
b, _ := c.GetRawData()
var m map[string]interface{}
// 反序列化
json.Unmarshal(b, &m)
c.JSON(200, m)
})
r.Run(":8085")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 5.5、Path参数
func TestPathParam(t *testing.T) {
r := gin.Default()
// Param获取
r.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
c.JSON(200, gin.H{
"name": name,
})
})
// MustGet获取(如果参数不存在,将会引发panic)
r.GET("/user/:id", func(c *gin.Context) {
id := c.MustGet("id").(string)
c.JSON(200, gin.H{
"id": id,
})
})
r.Run(":8085")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 5.6、参数绑定 ⭐
gin提供了灵活的bind解析参数的方法供你选择
解析错误回在header中写一个400的状态码
//内部根据Content-Type去解析
c.Bind(obj interface{})
//内部替你传递了一个binding.JSON,对象去解析
c.BindJSON(obj interface{})
//解析哪一种绑定的类型,根据你的选择
c.BindWith(obj interface{}, b binding.Binding)
1
2
3
4
5
6
7
2
3
4
5
6
7
解析错误直接返回,至于要给客户端返回什么错误状态码有你决定
//内部根据Content-Type去解析
c.ShouldBind(obj interface{})
//内部替你传递了一个binding.JSON,对象去解析
c.ShouldBindJSON(obj interface{})
//解析哪一种绑定的类型,根据你的选择
c.ShouldBindWith(obj interface{}, b binding.Binding)
1
2
3
4
5
6
7
2
3
4
5
6
7
例子:
func TestBindParam(t *testing.T) {
r := gin.Default()
r.POST("/login", func(c *gin.Context) {
var form LoginForm
// 使用 ShouldBind 绑定数据
if err := c.ShouldBind(&form); err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{
"username": form.Username,
"password": form.Password,
})
})
r.Run(":8085")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 6、文件上传
func TestFileUpload(t *testing.T) {
r := gin.Default()
// 处理multipart forms提交文件时默认的内存限制是32 MiB
// 可以通过下面的方式修改
// router.MaxMultipartMemory = 8 << 20 // 8 MiB
// 单个文件
r.POST("/upload", func(c *gin.Context) {
file, err := c.FormFile("f1")
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": err.Error(),
})
return
}
t.Log(file.Filename)
dst := fmt.Sprintf("./upload/%s", file.Filename)
// 上传文件到指定的目录
c.SaveUploadedFile(file, dst)
c.JSON(http.StatusOK, gin.H{
"message": fmt.Sprintf("'%s' uploaded!", file.Filename),
})
})
// 多个文件
r.POST("/uploads", func(c *gin.Context) {
// Multipart form
form, _ := c.MultipartForm()
files := form.File["file"]
for index, file := range files {
t.Log(file.Filename)
dst := fmt.Sprintf("./uploads/%s_%d", file.Filename, index)
// 上传文件到指定的目录
c.SaveUploadedFile(file, dst)
}
c.JSON(http.StatusOK, gin.H{
"message": fmt.Sprintf("%d files uploaded!", len(files)),
})
})
r.Run(":8085")
}
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
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
# 7、转发&重定向
func TestForward_Redirect(t *testing.T) {
r := gin.Default()
// 转发
r.GET("/forward", func(c *gin.Context) {
// 发起一个新的 HTTP GET 请求,转发给百度
resp, err := http.Get("http://baidu.com")
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
defer resp.Body.Close()
// 读取响应体
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// 将响应体返回给客户端
c.Data(resp.StatusCode, resp.Header.Get("Content-Type"), body)
})
// URL重定向
r.GET("/redirect", func(c *gin.Context) {
// 使用 302 临时重定向到另一个 URL
c.Redirect(http.StatusFound, "http://baidu.com")
})
r.GET("/permanent_redirect", func(c *gin.Context) {
// 使用 301 永久重定向到另一个 URL
c.Redirect(http.StatusMovedPermanently, "http://baidu.com")
})
// 路由重定向
// HandleContext 重新进入已重写的上下文。可以通过将 c.Request.URL.Path 设置为新目标,然后再次执行
r.GET("/redirect1", func(c *gin.Context) {
// 指定重定向的URL
c.Request.URL.Path = "/redirect2"
r.HandleContext(c)
})
r.GET("/redirect2", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"hello": "world"})
})
r.Run(":8085")
}
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
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
上次更新: 2024/07/08, 18:19:51