gin框架一套解决

[toc]

介绍

  • Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确,具有快速灵活,容错方便等特点
  • 对于golang而言,web框架的依赖要远比Python,Java之类的要小。自身的net/http足够简单,性能也非常不错
  • 借助框架开发,不仅可以省去很多常用的封装带来的时间,也有助于团队的编码风格和形成规范

要求

  • Go 1.13 及以上版本

安装

要安装 Gin 软件包,需要先安装 Go 并设置 Go 工作区。

1.下载并安装 gin:

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

💡 补充get参数:

附加参数备 注
-v显示操作流程的日志及信息,方便检查错误
-u下载丢失的包,但不会更新已经存在的包
-d只下载,不安装
-insecure允许使用不安全的 HTTP 方式进行下载操作

2.将 gin 引入到代码中:

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

3.(可选)如果使用诸如 http.StatusOK 之类的常量,则需要引入 net/http 包:

import "net/http"

4.创建你的项目文件夹并 cd 进去

如果是用的mod包,那么并不需要

$ mkdir -p $GOPATH/src/github.com/myusername/project && cd "$_"

5.拷贝一个初始模板到你的项目里

$ curl https://raw.githubusercontent.com/gin-gonic/examples/master/basic/main.go > main.go

网络不行可以直接访问 这里open in new window复制到main.go

6.运行你的项目

$ go run main.go

最后访问即可:

image-20221013225622549

开始

不确定如何编写和执行 Go 代码? 点击这里open in new window.

首先,创建一个名为 example.go 的文件

$ touch example.go

接下来, 将如下的代码写入 example.go 中:

package main

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

func main() {
	r := gin.Default()
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})
	r.Run() // 监听并在 0.0.0.0:8080 上启动服务
}

然后, 执行 go run example.go 命令来运行代码:

# 运行 example.go 并且在浏览器中访问 HOST_IP:8080/ping
$ go run example.go

🚀 编译结果如下:

image-20221013225920708

hello word

package main

import (
    "net/http"

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

func main() {
    // 1.创建路由
   r := gin.Default()
   // 2.绑定路由规则,执行的函数
   // gin.Context,封装了request和response
   r.GET("/", func(c *gin.Context) {
      c.String(http.StatusOK, "hello World!")
   })
   // 3.监听端口,默认在8080
   // Run("里面不指定端口号默认为8080") 
   r.Run(":8000")
}

image-20221013230032119

其他的请求和json

💡简单的一个案例如下:

// gin
package main

import (
	"net/http"

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

func main() {
	//创建gin实例
	r := gin.Default()
	//设置路由:请求方式,路径,处理函数
	r.GET("/ping", func(c *gin.Context) {
		c.String(200, "这是一个Get ping请求")
		c.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})

	//Post请求: http://localhost:8080/login 表示向服务器发送登录请求
	r.POST("/login", func(c *gin.Context) {
		c.String(200, "登录成功,这是一个Post请求")
		//获取参数
		username := c.PostForm("username")
		password := c.PostForm("password")
		//返回json数据
		c.JSON(http.StatusOK, gin.H{
			"username": username,
			"password": password,
		})
	})

	//Put请求: http://localhost:8080/put 表示向服务器发送修改请求
	r.PUT("/put", func(c *gin.Context) {
		c.String(200, "修改成功,这是一个Put请求")
		//获取参数
		username := c.PostForm("username") //当存在POST urlenencoded表单或multipart表单时,PostForm返回指定的键,否则返回空字符串'("")'
		password := c.PostForm("password")
		//返回json数据 : 传入的数据:状态码和空接口类型数据
		c.JSON(http.StatusOK, gin.H{ //gin.H是map[string]interface{}的简写,http.StatusOK是200
			"username": username,
			"password": password,
		})
	})

	//Delete请求: http://localhost:8080/delete 表示向服务器发送删除请求
	r.DELETE("/delete", func(c *gin.Context) {
		c.String(200, "删除成功,这是一个Delete请求")
		//获取参数
		username := c.PostForm("username")
		password := c.PostForm("password")
		//返回json数据
		c.JSON(http.StatusOK, gin.H{
			"username": username,
			"password": password,
		})
	})

	//监听端口

	// r.Run() 启动HTTP服务,默认在 0.0.0.0:8080 启动服务
	r.Run(":3001") //也可以使用这种方式设置端口
}

🚀 编译结果如下:

PS D:\文档\最近的\awesome-golang\docs\code\go-super\gin> air

  __    _   ___
 / /\  | | | |_)
/_/--\ |_| |_| \_ , built with Go

watching .
!exclude tmp
building...
running...
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /ping                     --> main.main.func1 (3 handlers)
[GIN-debug] POST   /login                    --> main.main.func2 (3 handlers)
[GIN-debug] PUT    /put                      --> main.main.func3 (3 handlers)
[GIN-debug] DELETE /delete                   --> main.main.func4 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :3001
[GIN] 2022/11/01 - 20:45:41 | 200 |            0s |       127.0.0.1 | PUT      "/put"

image-20221101204631005

相应 json 数据并且返回自定义 json 名称

// 定义结构体
type User struct {
	Username string `json:"用户名"`
	Password string `json:"密码"`
}	
// jaon
func main() {
	r.POST("/json", func(c *gin.Context) {
		//声明结构体
		a := &User{
			Username: "admin",
			Password: "123456",
		}
		c.JSON(http.StatusOK, a)
		//绑定参数
	})
}

🚀 编译结果如下:

{
    "用户名": "admin",
    "密码": "123456"
}

响应 jsonp 请求

💡简单的一个案例如下:

// 定义结构体
type User struct {
	Username string `json:"用户名"`
	Password string `json:"密码"`
}	
// jaon
func main() {
    //相应jsonp数据: http://localhost:3001/jsonp?callback=callback
	r.Get("/jsonp", func(c *gin.Context) {
		//声明结构体
		a := &User{
			Username: "admin",
			Password: "123456",
		}
		c.JSON(http.StatusOK, a)
		//绑定参数
	})
}

🚀 编译结果如下:

callback({"用户名":"admin","密码":"123456"});

加载yaml和xml

// 定义结构体
type User struct {
	Username string `json:"用户名"`
	Password string `json:"密码"`
}	
// jaon
func main() {
    //相应xml数据: http://localhost:3001/xml
	r.GET("/xml", func(c *gin.Context) {
		//声明结构体
		a := &User{
			Username: "admin",
			Password: "123456",
		}
		//返回xml数据
		c.XML(http.StatusOK, a)
	})

	//相应yaml数据: http://localhost:3001/yaml
	r.GET("/yaml", func(c *gin.Context) {
		//声明结构体
		a := &User{
			Username: "admin",
			Password: "123456",
		}
		//返回yaml数据
		c.YAML(http.StatusOK, a)
	})
}

🚀 编译结果如下:

#xml:
<User>
    <Username>admin</Username>
    <Password>123456</Password>
</User>

#yml
username: admin
password: "123456"

加载html

//对应html数据: http://localhost:3001/html
r.GET("/html", func(c *gin.Context) {
	//声明结构体
	a := &User{
		Username: "admin",
		Password: "123456",
	}
	//返回html数据
	c.HTML(http.StatusOK, "./web/index.html", gin.H{
		"test": a,
	})
})

gin框架采用的路由库

  • gin 框架中采用的路由库是基于httprouter做的
  • 地址为:https://github.com/julienschmidt/httprouter

Gin模板渲染

main.go 文件:

package main

import (
	"net/http"

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

type New struct {
	Title   string `json:"新闻标题"`
	Content string `json:"新闻内容"`
}

func main() {
	r := gin.Default()

	//html: http://localhost:8080/
	r.LoadHTMLGlob("templates/*") //表示加载templates文件夹下的所有文件
	r.GET("/", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.html", gin.H{
			"title": "Main website",
			"t":     "xiongxinwei",
		})
	})

	//html: http://localhost:8080/news
	r.GET("/news", func(c *gin.Context) {
		n := &New{
			Title:   "new title",
			Content: "new content",
		}

		c.HTML(http.StatusOK, "news.html", gin.H{
			"title": "News website",
			"news":  n,
			"t":     "t",
		})
	})

	r.Run()
}

index.html 文件:

<!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>这是index文件</h1>
    <!-- 渲染 -->
    <h2>{{.title}}</h2>

    <h3>t = {{.t}}</h3>
    {{$t = .title}}  <!-- 定义变量 -->
    {{$t}}  <!-- 使用变量 -->
    {{$t = 30 }}  <!-- 修改变量 -->
    {{$t}}  <!-- 使用变量 -->
    <h3>{{.t}}</h3>
</body>
</html>

news.html 文件:

<!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>这是news文件</h1>
    <!-- 渲染 -->
    <h2>{{.title}}</h2>
    <!-- 访问news -->
    <div>
        {{.news}}  <!-- 拿到结构体对象 -->
    </div>
    <div>
        {{.news.Title}}  <!-- 拿到结构体对象的属性 -->
    </div>
    <div>
        {{.news.Content}}  <!-- 拿到结构体对象的属性 -->
    </div>
    <div>
        
    </div>
</body>
</html>
🚀 编译结果如下:

image-20221102110508517

image-20221102110523505

image-20221102110542458

预定义函数

预定义函数浏览⚡

预定义的全局变量:

  1. 通过 {{.}} 访问结构体对象
  2. 通过 {{.属性名}} 访问结构体对象的属性
  3. . 当前对象
  4. .. 父对象
  5. index 当前对象的索引
  6. len 当前对象的长度
  7. urlquery 当前对象的URL编码
  8. js 当前对象的JavaScript编码
  9. html 当前对象的HTML编码
  10. json 当前对象的JSON编码
  11. xml 当前对象的XML编码
  12. yaml 当前对象的YAML编码
  13. csv 当前对象的CSV编码
  14. tsv 当前对象的TSV编码
  15. urlpath 当前对象的URL路径编码
  16. slice 当前对象的切片
  17. first 当前对象的第一个元素
  18. last 当前对象的最后一个元素
  19. sort 当前对象的排序
  20. reverse 当前对象的反转
  21. and 当前对象的逻辑与
  22. or 当前对象的逻辑或
  23. not 当前对象的逻辑非
  24. eq 当前对象的等于
  25. ne 当前对象的不等于
  26. lt 当前对象的小于
  27. le 当前对象的小于等于
  28. gt 当前对象的大于
  29. ge 当前对象的大于等于
  30. add 当前对象的加法
  31. sub 当前对象的减法
  32. mul 当前对象的乘法
  33. div 当前对象的除法
  34. mod 当前对象的取模
  35. call 当前对象的函数调用
  36. print 当前对象的打印
  37. printf 当前对象的格式化打印
  38. println 当前对象的换行打印

常用的内置函数和自定义模板函数

💡简单的一个案例如下(main.go 文件):

package main

import (
	"fmt"
	"html/template"
	"net/http"

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

type New struct {
	Title   string `json:"this is 新闻标题"`
	Content string `json:"this is 新闻内容"`
}

// 自定义模板函数
func Text(title string) string {
	fmt.Println("title:", title)
	return title + " this is 自定义模板函数Text"
}

func Text2(title string, content string) string {
	fmt.Println("title:", title, "content:", content)
	return title + " " + content + " this is 自定义模板函数Text2"
}

func main() {
	r := gin.Default()

	// 加载自定义模板函数
	r.SetFuncMap(template.FuncMap{
		"Text":  Text,
		"Text2": Text2,
	})

	//html: http://localhost:8080/
	r.LoadHTMLGlob("templates/*") //表示加载templates文件夹下的所有文件
	r.GET("/", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.html", gin.H{
			"title":  "this is a title",
			"msg":    "this is a msg",
			"score":  99,                          //score是int类型,表示分数
			"score2": 99.9,                        //score2是float64类型,表示分数
			"score3": "99.9",                      //score3是string类型,表示分数
			"hobby":  []string{"篮球", "足球", "乒乓球"}, //hobby是切片类型,表示爱好  //hobby是切片类型,表示爱好
			"map": map[string]interface{}{ //map是map类型,表示地图
				"address": "北京市海淀区",
				"tel":     "010-12345678",
			},

			//结构体
			"news": &New{
				Title:   "新闻标题",
				Content: "新闻内容",
			},
		})
	})

	r.Run()
}

index.html 文件:

<!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>
    <!-- 缩小h6间距 -->
    <style>
        h6 {
            margin: 0;
        }
    </style>

</head>
<body>
    <h3>这是index文件</h3>
    <!-- 渲染 -->
    <h6>titls = {{.title}}</h6>
    <h6>msg = {{.msg}}</h6>
    <h6>score(int类型分数) = {{.score}}</h6>
    <h6>score2(float64类型分数) = {{.score2}}</h6>
    <h6>score3(string类型分数) = {{.score3}}</h6>
    <h6>hobby(切片类型爱好) = {{.hobby}}</h6>
    <h6>map(地图) = {{.map}}</h6>
    <h6>news(结构体) = {{.news}}</h6>
    <h6>news.Title(结构体属性) = {{.news.Title}}</h6>

    <!-- 求msg长度 -->
    <h6>msg长度 = {{len .msg}}</h6>

    <!-- 判断msg是否为空 -->
    <h6>msg是否为空 = {{if .msg}}不为空{{else}}为空{{end}}</h6>

    <!-- 判断score是否大于60 -->
    <h6>score是否大于60 = {{if gt .score 60}}大于60{{else}}小于60{{end}}</h6>

    <!-- 遍历hobby -->
    <h6>hobby(切片类型爱好) = {{range .hobby}}{{.}} {{end}}</h6>

    <!-- 遍历map -->
    <h6>map(地图) = {{range $key, $value := .map}}{{$key}} = {{$value}} {{end}}</h6>

    <!-- 自定义模板函数
        直接在模板中调用,比如下面的函数,直接在模板中调用
    -->
    <h3>自定义模板函数/h3>
    <h6>自定义模板函数 = {{Text .title}}</h6>
    <h6>自定义模板函数2 = {{Text2 .title .msg}}</h6>
</body>
</html>

🚀 编译结果如下:

image-20221102115622008

加载一个公共的模板

加载一个公共的模板,也可以叫做嵌套 template

我们一个项目里面可能有一个公共的头部或者尾部,就像我们这个仓库一样,每一页都有一个模板:

image-20221102120417428

在每个html 文件的任意位置都可以使用, 注意后面的 .

<!-- 公共模板 头部-->
{{template "public/page_header.html" .}}
    
<!-- 公共模板 尾部-->
{{template "public/page_footer.html" .}}

public/page_header.html 文件

<!-- 相当于给模板定义一个名字,define end 成对出现 -->
{{define "public/page_foorter"}}
<!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>
    <style>
        h1{
            color: blue;
            background-color: yellow;  /* 背景色 */
            text-align: center;  /* 文字居中 */
        }
    </style>
</head>
<body>
    <h1>this is a public test page - footer</h1>

</body>
</html>

public/page_footer.html 文件:

<!-- 相当于给模板定义一个名字,define end 成对出现 -->
{{define "public/page_header"}}
<!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>
    <style>
        h1{
            color: red;
            background-color: yellow;  /* 背景色 */
            text-align: center;  /* 文字居中 */
        }
    </style>
</head>
<body>
    <h1>this is a public test page</h1>
</body>
</html>

路由传值

路由(Routing)是由一个 URL 和 HTTP 方法(get、post)等组成

Get传值

💡简单的一个案例如下:

// Get传值
//html: http://localhost:8080/newst?title=新闻标题&content=新闻内容
r.GET("/newst", func(c *gin.Context) {
	title := c.Query("title")
	content := c.Query("content")

	c.JSON(http.StatusOK, gin.H{
		"title":   title,
		"content": content,
	})
})

🚀 编译结果如下:

image-20221106183736547

**同样也可以传递给 html **

END 链接