GoWeb开发的gin框架学习 part one

Posted by OuterCyrex on June 1, 2024

GoWeb开发的一大利器——gin框架的一些学习记录 第一部分。

一.响应

一.基本语句

1.Router参数

路由(Router)参数 用于处理请求

其决定了当客户端(如浏览器或API调用者)发送请求到服务器时,服务器如何根据请求的URLHTTP方法来执行相应的逻辑处理

2.代码实现

用代码实现一个写有Hello,World!字样的网页,并在localhost:8080上运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

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

func main() {

	//首先创建一个路由
	router := gin.Default()

	//绑定路由规则和路由函数,访问/index的路由,将有对应的函数去处理
	router.GET("/index", func(context *gin.Context) {
		context.String(200, "Hello,World!")
		//状态码 http.StatusOK = 200  表示正常状态
	})

	//启动接听,gin会把web服务运行在localhost:8080上
	_ = router.Run(":8080")
	//或http.ListenAndServe(":8080",router) 基于http/net
}

其中,http/net包自带的状态码包括:

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
const (
	StatusContinue           = 100 // RFC 9110, 15.2.1
	StatusSwitchingProtocols = 101 // RFC 9110, 15.2.2
	StatusProcessing         = 102 // RFC 2518, 10.1
	StatusEarlyHints         = 103 // RFC 8297

	StatusOK                   = 200 // RFC 9110, 15.3.1
	StatusCreated              = 201 // RFC 9110, 15.3.2
	StatusAccepted             = 202 // RFC 9110, 15.3.3
	StatusNonAuthoritativeInfo = 203 // RFC 9110, 15.3.4
	StatusNoContent            = 204 // RFC 9110, 15.3.5
	StatusResetContent         = 205 // RFC 9110, 15.3.6
	StatusPartialContent       = 206 // RFC 9110, 15.3.7
	StatusMultiStatus          = 207 // RFC 4918, 11.1
	StatusAlreadyReported      = 208 // RFC 5842, 7.1
	StatusIMUsed               = 226 // RFC 3229, 10.4.1

	StatusMultipleChoices   = 300 // RFC 9110, 15.4.1
	StatusMovedPermanently  = 301 // RFC 9110, 15.4.2
	StatusFound             = 302 // RFC 9110, 15.4.3
	StatusSeeOther          = 303 // RFC 9110, 15.4.4
	StatusNotModified       = 304 // RFC 9110, 15.4.5
	StatusUseProxy          = 305 // RFC 9110, 15.4.6
	_                       = 306 // RFC 9110, 15.4.7 (Unused)
	StatusTemporaryRedirect = 307 // RFC 9110, 15.4.8
	StatusPermanentRedirect = 308 // RFC 9110, 15.4.9

	StatusBadRequest                   = 400 // RFC 9110, 15.5.1
	StatusUnauthorized                 = 401 // RFC 9110, 15.5.2
	StatusPaymentRequired              = 402 // RFC 9110, 15.5.3
	StatusForbidden                    = 403 // RFC 9110, 15.5.4
	StatusNotFound                     = 404 // RFC 9110, 15.5.5
	StatusMethodNotAllowed             = 405 // RFC 9110, 15.5.6
	StatusNotAcceptable                = 406 // RFC 9110, 15.5.7
	StatusProxyAuthRequired            = 407 // RFC 9110, 15.5.8
	StatusRequestTimeout               = 408 // RFC 9110, 15.5.9
	StatusConflict                     = 409 // RFC 9110, 15.5.10
	StatusGone                         = 410 // RFC 9110, 15.5.11
	StatusLengthRequired               = 411 // RFC 9110, 15.5.12
	StatusPreconditionFailed           = 412 // RFC 9110, 15.5.13
	StatusRequestEntityTooLarge        = 413 // RFC 9110, 15.5.14
	StatusRequestURITooLong            = 414 // RFC 9110, 15.5.15
	StatusUnsupportedMediaType         = 415 // RFC 9110, 15.5.16
	StatusRequestedRangeNotSatisfiable = 416 // RFC 9110, 15.5.17
	StatusExpectationFailed            = 417 // RFC 9110, 15.5.18
	StatusTeapot                       = 418 // RFC 9110, 15.5.19 (Unused)
	StatusMisdirectedRequest           = 421 // RFC 9110, 15.5.20
	StatusUnprocessableEntity          = 422 // RFC 9110, 15.5.21
	StatusLocked                       = 423 // RFC 4918, 11.3
	StatusFailedDependency             = 424 // RFC 4918, 11.4
	StatusTooEarly                     = 425 // RFC 8470, 5.2.
	StatusUpgradeRequired              = 426 // RFC 9110, 15.5.22
	StatusPreconditionRequired         = 428 // RFC 6585, 3
	StatusTooManyRequests              = 429 // RFC 6585, 4
	StatusRequestHeaderFieldsTooLarge  = 431 // RFC 6585, 5
	StatusUnavailableForLegalReasons   = 451 // RFC 7725, 3

	StatusInternalServerError           = 500 // RFC 9110, 15.6.1
	StatusNotImplemented                = 501 // RFC 9110, 15.6.2
	StatusBadGateway                    = 502 // RFC 9110, 15.6.3
	StatusServiceUnavailable            = 503 // RFC 9110, 15.6.4
	StatusGatewayTimeout                = 504 // RFC 9110, 15.6.5
	StatusHTTPVersionNotSupported       = 505 // RFC 9110, 15.6.6
	StatusVariantAlsoNegotiates         = 506 // RFC 2295, 8.1
	StatusInsufficientStorage           = 507 // RFC 4918, 11.5
	StatusLoopDetected                  = 508 // RFC 5842, 7.2
	StatusNotExtended                   = 510 // RFC 2774, 7
	StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6
)

3.响应

“响应”(Response)通常指的是服务器客户端(如Web浏览器、移动应用或API调用者)发出的请求(Request)所做的回复。这个回复包含了服务器处理请求后生成的数据,以及关于这些数据的一些元信息

二.数据序列化格式处理

1.json

a.结构体

下面用结构体类型来响应GET指令,返回一段JSON数据

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
package main

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

func _json(c *gin.Context) {
	type UserInfo struct {
		Username string `json:"username"`
		Age      int    `json:"age"`
		Password string `json:"-"` //"-"表示忽略转换为json
	} //定义一个结构体用于json转化
    
	var Test UserInfo
	Test = UserInfo{
		Username: "Outer",
		Age:      20,
		Password: "123456",
	} //为结构体赋值
    
	c.JSON(200, Test) //将Test由结构体类型转化为JSON格式
}
func main() {
	router := gin.Default()
	router.GET("/json", _json)
	err := router.Run(":8080")
	if err != nil {
		panic(err)
	}
}

返回值:

1
2
3
4
{
    "username": "Outer",
    "age": 20
}

b.映射

通过构建map[string]string来表示json数据中的keyvalue关系

1
2
3
4
5
6
7
func _json(c *gin.Context) {
    userMap := map[string]interface{}{
		"username": "Outer",
		"age":      18,
	}
	c.JSON(http.StatusOK, userMap)
}

返回值:

1
2
3
4
{
    "age": 18,
    "username": "Outer"
}

由于Go中的map是无序的,并且这个顺序对于用户来说是不透明的。

但对于JSON来说,属性的顺序通常并不重要,因为JSON是一个基于文本的数据交换格式,它定义的是数据的结构而不是数据的展示顺序

c.输出gin.H{}

通过gin自带的gin.H来输出json值

gin基层代码中是这要描述gin.H

1
2
// H is a shortcut for map[string]any
type H map[string]any

gin.H的本质是上述的映射,即map[string]interface{}

1
2
3
4
5
6
func _json(c *gin.Context) {
	c.JSON(200, gin.H{
		"username": "Outer",
		"age":      "18",
	})
}

返回值:

1
2
3
4
{
    "age": "18",
    "username": "Outer"
}

2.xml

具体的函数可以依照json的写法,此处以gin.H输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

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

func main() {
	router := gin.Default()
	router.GET("/xml", func(c *gin.Context) {
		c.XML(http.StatusOK, gin.H{
			"username": "Outer",
			"age":      "18",
		})
	})
	_ = router.Run(":8080")
}

返回值:

1
2
3
4
<map>
    <username>Outer</username>
    <age>18</age>
</map>

3.yaml

具体的函数可以依照json的写法,此处以gin.H输出

1
2
3
4
5
6
7
8
9
10
func main() {
	router := gin.Default()
	router.GET("/yaml", func(c *gin.Context) {
		c.YAML(http.StatusOK, gin.H{
			"username": "Outer",
			"age":      "18",
		})
	})
	_ = router.Run(":8080")
}

返回值:

1
2
3
4
<map>
    <username>Outer</username>
    <age>18</age>
</map>

三.其他形式的响应

1.响应HTML

首先定义一个html文件,相对路径templates/index.html(注:在go中,相对路径是相对于项目目录

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<header>HELLO 
</header>
</body>
</html>

其中``````用于接受一个username值并做出替换

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

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

// 响应html
func _html(c *gin.Context) {
	c.HTML(http.StatusOK, "index.html", gin.H{"username": "Outer"}) //gin.H可以传参
}

func main() {
	router := gin.Default()
	//加载模版目录下所有html文件
	router.LoadHTMLGlob("templates/*")
	router.GET("/html", _html)
	//在golang中,没有相对文件的路径,只有相对项目的路径
	_ = router.Run(":8080")
}

返回值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>

<body>
    <header>HELLO Outer
    </header>
</body>

</html>

2.响应文件

现假定项目目录的pic路径存有一张xdzn.png图片文件

现发出一个请求,要求返回文件xdzn.png

1
2
3
4
5
6
7
8
9
func main(){
    router := gin.Default()
	router.StaticFile("/pic", "./pic/xdzn.png")
	//StaticFile用于访问路径对应的文件,即配置单个文件
	router.StaticFS("/static", http.Dir("./pic"))
	//StaticFS用于访问路径对应的所有文件,将整个文件夹的内容返回
	//如可写localhost:8080/static/xdzn.png
	_ = router.Run(":8080")
}

此时,可以通过两种URL路径获得这张png:

1
2
"localhost:8080/pic"
"localhost:8080/static/xdzn.png"

若访问"localhost:8080/static",则将返回html数据:

1
2
3
<pre>
<a href="xdzn.png">xdzn.png</a>
</pre>

3.响应跳转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

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

func _redirect(c *gin.Context) {
	c.Redirect(301, "http://www.baidu.com")
}
func main() {
	router := gin.Default()
	router.GET("/baidu", _redirect)
	_ = router.Run(":8080")
}

通过gin包中的Redirect方法来实现页面的跳转

上述代码可实现 当访问localhost:8080/baidu时跳转至http://www.baidu.com


此处状态码301状态码302的区别

  • 301 Moved Permanently(永久重定向):这个状态码表示请求的URL已经被永久地移动到了由Location头部指定的新URL上。这意味着将来所有对该资源的引用都应该使用新的URI。搜索引擎在抓取到301状态码后,会将旧的URL替换为新的URL,并将旧URL的权重和排名转移到新URL上。
  • 302 Found(临时重定向):这个状态码表示请求的URL暂时被移动到了由Location头部指定的新URL上。这通常发生在服务器暂时性地修改了资源的位置,但未来可能会恢复原始位置。搜索引擎在抓取到302状态码后,通常不会将旧的URL替换为新的URL,而是继续索引旧的URL,并在用户访问时将其重定向到新的URL。