Golang|Gin Web development for Beginners

注意
本文最后更新于 2022-02-11,文中内容可能已过时。

http库提供了HTTP服务的用户端和服务端的实现。

官方文档

Go语言基础之net/http | 李文周的博客 (liwenzhou.com)

示例代码

监听本地端口,在浏览器输出Hello World字符串。

1
2
3
4
5
6
7
8
func sayHello(w http.ResponseWriter,r *http.Request){
    //ResponseWriter为服务端返回的内容
	fmt.Fprintln(w, "Hello World!")
}
func main(){
	http.HandleFunc("/",sayHello)
	http.ListenAndServe(":8080",nil)
}

Gin 是一个用 Go (Golang) 编写的 HTTP web 框架。 它是一个类似于 martini 但拥有更好性能的 API 框架, 优于 httprouter,速度提高了近 40 倍。如果你需要极好的性能,使用 Gin 吧。

官方中文文档

Gin框架介绍及使用 | 李文周的博客 (liwenzhou.com)

特性

  • 零分配路由。
  • 仍然是最快的 http 路由器和框架。
  • 完整的单元测试支持。
  • 实战考验。
  • API 冻结,新版本的发布不会破坏你的代码。

使用Gin框架返回一个json文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
func sayHello(c *gin.Context/*gin框架中的临时变量,便于后续响应请求*/){
	c.JSON(http.StatusOK,gin.H{//返回一个json数据
		"message":"HelloWorld",
	})
}
func main(){
	r:=gin.Default()//获取gin框架默认路由对象
	r.GET("/",sayHello)//处理向"/"目录发起的get请求,并将其使用sayHello函数处理
	r.Run()
}

项目实践中一般使用匿名函数:

1
2
3
4
5
r.GET("/",func(c *gin.Context){
    c.JSON(http.StatusOK,gin.H{
        "message":"HelloWorld",
    })
})

使用Gin框架返回一个html文件

1
2
3
4
5
6
7
8
func main(){
	r:=gin.Default()
	r.LoadHTMLFiles("hello.html")//解析模板
	r.GET("/",func(c *gin.Context){
		c.HTML(http.StatusOK,"hello.html",gin.H{})
	})
	r.Run()
}

template package - html/template - pkg.go.dev

Go语言标准库之http/template | 李文周的博客 (liwenzhou.com)

一个简单示例

 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
type User struct {
    //成员变量名必须为大写,使其可以被外部访问
	Name string 
	Gender string
	Age int
}
func sayHello(w http.ResponseWriter, r *http.Request){
	// 定义模板
	// 解析模板
	t, _ := template.ParseFiles("./hello.tmpl")
	// 渲染模板
	u1 := User{  // u1.Name
		Name:   "zeroy",
		Gender: "男",
		Age:    18,
	}
	m1 := map[string]interface{}{
		"name": "zeroy",
		"gender": "男",
		"age": 18,
	}
	hobbyList := []string{
		"乒乓球",
		"网球",
		"羽毛球",
	}
	t.Execute(w, map[string]interface{}{
		"u1": u1,
		"m1": m1,
		"hobby": hobbyList,
	})
}

func main() {
	http.HandleFunc("/", sayHello)
	http.ListenAndServe(":9000", nil)
}

在Gin框架中渲染模板

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
func main(){
	r:=gin.Default()
	r.LoadHTMLFiles("hello.tmpl")//加载模板
	r.GET("/",func(c *gin.Context){
		c.HTML(http.StatusOK,"hello.tmpl",gin.H{//向模板中传递参数
			"name": "zeroy",
			"age": 18,
		})
	})
	r.Run()
}

模板文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>name: {{.name}}</h1>
    <h1>age: {{.age}}</h1>
</body>
</html>

由于实际开发中大多采用前后端分离式开发,模板技术的应用范围不大,所以这里不再赘述,有需要直接查文档就好。

 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
package main
import (
	"github.com/gin-gonic/gin"
	"net/http"
)
func main() {
	r := gin.Default()
	r.GET("/json", func(c *gin.Context) {
		data := gin.H{"name":"zeroy", "message": "hello world!", "age": 18}
		c.JSON(http.StatusOK, data)
	})
	type msg struct{
		Name string `json:"name"`//有时为了方便前端开发,需要为类型名创建别名(反射)
		Message string
		Age int
	}
	r.GET("/another_json", func(c *gin.Context) {
		data := msg{
			"zeroy",
			"Hello golang!",
			18,
		}
		c.JSON(http.StatusOK, data)//本质是msg的序列化
	})
	r.Run()
}

querystring参数即URL中?后的参数。

1
2
username := c.DefaultQuery("username", "zeroy")//若查询不到username则默认值为zeroy
address := c.Query("address")

例程:A+B Problem web版

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main
import (
	"github.com/gin-gonic/gin"
	"strconv"
)
func main() {
	r:=gin.Default()
	r.GET("/",func (c *gin.Context)  {
		a,_:=strconv.Atoi(c.Query("a"))
		b,_:=strconv.Atoi(c.Query("b"))
		c.JSON(200,gin.H{"sum":a+b,})
	})
	r.Run()
}
1
2
username := c.PostForm("username")
address := c.PostForm("address")

PostForm方法可以获取Post请求提交的参数。

例程:A+B Problem web版(使用path参数传参)

1
2
3
4
5
6
7
8
9
func main() {
	r:=gin.Default()
	r.GET("/:a/:b",func (c *gin.Context)  {
		a,_:=strconv.Atoi(c.Param("a"))
		b,_:=strconv.Atoi(c.Param("b"))
		c.JSON(200,gin.H{"sum":a+b,})
	})
	r.Run()
}

ShouldBind方法可以自动化绑定参数到某个struct中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
type People struct{
	Name string `form:"name"`//设定别名,方便传参
	Age int	`form:"age"`
	BirthDay string `form:"birthday"`
}
func main() {
	r:=gin.Default()
	r.GET("/", func(c *gin.Context) {
		var zeroy People
        //传递的name,age,birthday等变量可以自动化绑定到zeroy中
		if err := c.ShouldBind(&zeroy); err == nil {
			c.JSON(http.StatusOK, gin.H{
				"name":zeroy.Name,
				"age":zeroy.Age,
			})
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		}
	})
	r.Run()
}

例程:A+B problem with 参数绑定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
type Node struct{
	X int `form:"x"` 
	Y int `form:"y"`
}
func (node Node) sum() int{
	return node.X+node.Y
}
func main() {
	r:=gin.Default()
	r.GET("/", func(c *gin.Context) {
		var tmp Node
		if err := c.ShouldBind(&tmp); err == nil {
			c.JSON(http.StatusOK, gin.H{
				"sum":tmp.sum(),
			})
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		}
	})
	r.Run()
}
1
2
3
4
c.FormFile("")
dst := fmt.Sprintf("./%s", file.Filename)
// 上传文件到指定的目录
c.SaveUploadedFile(file, dst)
1
2
form, _ := c.MultipartForm()//多个文件上传
files := form.File["file"]

掌握了Gin框架的基本操作,我们便有能力搭建一个最最基本的web服务,处理来自用户端的请求。

但是如果要搭建一个真正实用的服务器后端,还需要与本地的数据库进行交互(database/sqlGORM),进行复杂的鉴权操作。

之后可能会看看数据库交互和一些中间件,实现鉴权和与本地数据库交互。