Gin框架相关笔记

简介

Gin是一个golang的微框架,封装比较优雅,,AP友好,源码注释比较明确。具有快速灵活,容错方便等特点。其实对于golang而言,web框架的依赖要远比Python,Java之类的要小。自身的net/http足够简单,性能也非常不错。框架更像是一些常用函数或者工具的集合。借助框架开发,不仅可以省去很多常用的封装带来的时间,也有助于团队的编码风格和形成规范。 Gin框架是开源的,可以在github 上下载其源码库,查看相应的说明。

github:https://github.com/gin-gonic/gin

文档:https://gin-gonic.com/zh-cn/docs/introduction/

 

特点

  • 速度:基于 Radix 树的路由,小内存占用。没有反射。可预测的 API 性能
  • 中间件:传入的 HTTP 请求可以由一系列中间件和最终操作来处理。 例如:Logger,Authorization,GZIP,最终操作 DB
  • 路由:在gin中可以非常简单的实现路由解析的功能,并包含路由组解析功能
  • 内置渲染:Gin 为 JSON,XML 和 HTML 渲染提供了易于使用的 API

 

安装

通过 go get 命令安装gin框架

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

安装完毕后,可以在当前系統的$GOPATH目录下的src/github.com目录中找到gin-gonic目录,该目录下存放的就是gin框架的源码。 安装完毕后,我们可以使用gin来写一个简单的demo程序。使用一下gin。

注意:如使用vscode编辑器安装框架时报错需在项目下创建go.mod文件

module demo

go 1.19  //go版本

 

GET请求

func main() {
	r := gin.Default()
	r.GET("/get", getMsg)
	//r.Run("localhost:9090")
	r.Run(":9090") //不指定IP地址,默认为本地

}

func getMsg(c *gin.Context) {
	name := c.Query("name")
	//字符串数据
	//c.String(http.StatusOK, "欢迎你哈哈哈:%s", name)

	//JSON数据
	c.JSON(http.StatusOK, gin.H{
		"code": http.StatusOK,
		"msg":  "成功",
		"data": "欢迎你:" + name,
	})
}

 

POST请求

func main() {
	r := gin.Default() // 路由引擎
	r.POST("/post", postMsg)
	r.Run(":9090")
}

func postMsg(c *gin.Context) {
	//name := c.Query("name") // 获取url中的数据 query
	name := c.DefaultPostForm("name", "gin") // 方法1
	fmt.Println(name)                        // hello
	from, b := c.GetPostForm("name")         // 方法2
	fmt.Println(from, b)                     // hello true   值+是否存在

	c.JSON(http.StatusOK, "欢迎您:"+name)
}

 

重定向

func main() {
	r := gin.Default()
	//一般重定向  重定向到外部网络
	r.GET("/redirect1", func(c *gin.Context) {
		url := "http://baidu.com"
		//重定向状态码StatusMovedPermanently
		c.Redirect(http.StatusMovedPermanently, url)
	})

	//路由重定向
	r.GET("/redirect2", func(c *gin.Context) {
		c.Request.URL.Path = "/TestRedirect"
		r.HandleContext(c)
	})

	r.GET("/TestRedirect", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"code": http.StatusOK,
			"msg":  "成功",
		})
	})
	r.Run(":9090")
}

 

返回第三方数据

func main() {
	r := gin.Default()
	r.GET("/GetOtherData", func(c *gin.Context) {
		url := "https://res.eemu.cn/LightPicture/2022/09/75b07814633a9d8a.jpeg"
		response, err := http.Get(url)
		if err != nil || response.StatusCode != http.StatusOK {
			c.Status(http.StatusServiceUnavailable)
			return
		}
		body := response.Body
		contentLength := response.ContentLength
		contentType := response.Header.Get("Content-Type")
		//	数据写入响应体
		c.DataFromReader(http.StatusOK, contentLength, contentType, body, nil)
	})
	r.Run(":9090")
}

 

多形式渲染

func main() {
	r := gin.Default()
	//JSON渲染
	r.GET("/json", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"html": "<h1>hello 捕风阁</h1>",
		})
	})

	//HTML渲染
	r.GET("/html", func(c *gin.Context) {
		c.PureJSON(200, gin.H{
			"html": "<h1>hello 捕风阁</h1>",
		})
	})

	//XML渲染
	r.GET("/xml", func(c *gin.Context) {
		type Message struct {
			Name string
			Msg  string
			Age  int
		}
		info := Message{}
		info.Name = "张三"
		info.Msg = "hello"
		info.Age = 23
		c.XML(http.StatusOK, info)
	})

	//YAML渲染
	r.GET("/yaml", func(c *gin.Context) {
		c.YAML(http.StatusOK, gin.H{
			"message": "YAML渲染",
			"status":  200,
		})
	})
	r.Run(":9090")
}

 

文件服务器

对于Client请求的内容,如果是视频、音频、图片等文件,该如何操作?

func main() {
	r := gin.Default()
	r.GET("/file", fileServer)
	r.Run(":9090")
}

func fileServer(c *gin.Context) {
	path := "./" 		// 路径
	fileName := path + c.Query("name") // 获取文件名称
	c.File(fileName)
}

http://localhost:9090/file?name=demo.jpg  即可看到当前目录下的图片

 

单文件上传

func main() {
	r := gin.Default()
	r.POST("/upload", func(c *gin.Context) {
		file, err := c.FormFile("fileName")
		if err != nil {
			c.String(http.StatusBadRequest, "文件上传错误") // 返回400错误
		}
		//存储路径
		dst := "D:/test"
		err2 := c.SaveUploadedFile(file, dst+file.Filename)
		if err2 != nil {
			c.String(http.StatusBadRequest, "文件上传错误2") // 返回400错误
		} //存储文件
		c.String(http.StatusOK, fmt.Sprintf("%s 上传完成", file.Filename))
	})
	r.Run(":9090")
}

Postman上传调试

图片[1] - Gin框架相关笔记 - 捕风阁

图片[2] - Gin框架相关笔记 - 捕风阁

 

多文件上传

func main() {
	r := gin.Default()
	r.POST("/upload", func(c *gin.Context) {
		form, err := c.MultipartForm() // 获取form
		if err != nil {
			c.String(http.StatusOK, "上传文件错误")
		}
		files := form.File["file_key"] // 上传的所有文件
		dst := "./"
		//	遍历文件
		for _, file := range files {
			c.SaveUploadedFile(file, dst+file.Filename)
		}
		c.String(http.StatusOK, fmt.Sprintf("%d 个文件上传完成", len(files)))
	})

	r.Run(":9090")
}

图片[3] - Gin框架相关笔记 - 捕风阁

 

自定义中间件

可以通过中间件对路由到来的数据先进行处理,包括数据加载、过滤等

我们通过中间件处理年龄及用户名,如下:

func main() {
	r := gin.Default() // 默认路由引擎 包括 Logger 和 Recovery 中间件
	//r := gin.New()     // 没有任何中间件的路由引擎
	r.Use(Middleware())
	r.GET("/middleware", func(c *gin.Context) {
		fmt.Println("服务端开始处理...")
		name := c.Query("name")
		ageStr := c.Query("age")
		age, _ := strconv.Atoi(ageStr)
		log.Println(name, age)
		res := struct {
			Name string `json:"name"` // 为结构添加标签 在JSON中显示为name
			Age  int    `json:"age"`
		}{name, age}
		c.JSON(http.StatusOK, res)
	})
	r.Run(":9090")
}

func Middleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		fmt.Println("中间件开始处理...")
		name := c.Query("name")
		ageStr := c.Query("age")
		age, err := strconv.Atoi(ageStr)
		if err != nil {
			c.AbortWithStatusJSON(http.StatusBadRequest, "输入的数据错误,年龄不是整数")
			return
		}
		if age < 0 || age > 100 {
			c.AbortWithStatusJSON(http.StatusBadRequest, "输入的数据错误,年龄数据错误")
			return
		}
		if len(name) < 6 || len(name) > 12 {
			c.AbortWithStatusJSON(http.StatusBadRequest, "用户名只能是6-12位")
			return
		}
		c.Next() // 执行后续操作
		fmt.Println(name, age)
	}
}

 

登陆中间件

gin框架同时提供了快速登陆验证中间件,可以完成登陆的验证

func main() {
	r := gin.Default()
	//路由使用gin.BasicAuth()中间件
	r.Use(AuthMiddleware())
	r.GET("/login", func(c *gin.Context) {
		//获取用户,它是由BasicAuth 中间件设置的
		user := c.MustGet(gin.AuthUserKey).(string)
		c.JSON(http.StatusOK, "登陆成功"+user)
	})
	r.Run(":9090")
}

func AuthMiddleware() gin.HandlerFunc {
	//初始化用户
	accounts := gin.Accounts{ //gin.Accounts 是map[string]string类型
		"admin":  "adminpw",
		"system": "systempw",
	}
	//动态添加用户
	accounts["go"] = "123456789"
	accounts["gin"] = "gin123"
	//	将用户添加到登陆中间件中
	auth := gin.BasicAuth(accounts)
	return auth
}

 

同步异步

同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。

异步方法调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作。而异步方法通常会在另外一个g0程 (goroutine) 过程,不会阻碍调用者的工作。

func main() {
	r := gin.Default()
	//同步
	r.GET("/sync", func(c *gin.Context) {
		sync(c)
		c.JSON(200, "主程序(主go程)同步已经执行")
	})

	//异步 通过go程执行
	r.GET("/async", func(c *gin.Context) {
		for i := 0; i < 10; i++ {
			cCp := c.Copy()
			go async(cCp, i)
		}
		c.JSON(200, "主程序(主go程)异步已经执行")
	})
	r.Run(":9090")
}

func sync(c *gin.Context) {
	println("开始执行同步任务:" + c.Request.URL.Path)
	time.Sleep(time.Second * 3)
	println("同步任务执行完成")
}

func async(cp *gin.Context, i int) {
	fmt.Println("第" + strconv.Itoa(i) + "个go主程开始执行" + cp.Request.URL.Path)
	time.Sleep(time.Second * 3)
	println("第" + strconv.Itoa(i) + "个go主程开始执行结束")
}

 

多服务器程序运行

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"golang.org/x/sync/errgroup"
	"net/http"
	"time"
)

// 定义路由组
var g errgroup.Group

func main() {
	//服务器1
	server01 := &http.Server{
		Addr:         ":9091",
		Handler:      router01(),
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
	}
	//服务器2
	server02 := &http.Server{
		Addr:         ":9092",
		Handler:      router02(),
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
	}
	//	开启服务
	g.Go(func() error {
		return server01.ListenAndServe()
	})
	g.Go(func() error {
		return server02.ListenAndServe()
	})
	//阻塞主go程
	if err := g.Wait(); err != nil {
		fmt.Println("执行失败")
	}
}

func router01() http.Handler {
	r1 := gin.Default()
	r1.GET("/MyServer", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"code": http.StatusOK,
			"msg":  "服务器程序1",
		})
	})
	return r1
}
func router02() http.Handler {
	r2 := gin.Default()
	r2.GET("/MyServer", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"code": http.StatusOK,
			"msg":  "服务器程序1",
		})
	})
	return r2
}

//TOD0:解洪包:golang.org/x/sync/errgroup 无法 goget的问题
//cd $GOPATH/trc/golang.org/x
//git clone https://github.com/golang/sync.git
//git clone https://github.com/golang/crypto.git
//git clone https://github.com/golang/sys.git

 

路由组

路由组可以方便的对路由进行分组和有效的分类,使路由对应的代码易阅读

package main

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

type ResGroup struct { // 定义结构体
	Data string
	Path string
}

func main() {
	router := gin.Default()
	//	路由分组1
	v1 := router.Group("/v1") // 分组1 1级路径
	{
		r := v1.Group("/user")        // 路由分组(2级路径)
		r.GET("/login", login)        // 响应请求 /v1/user/login
		r2 := r.Group("showInfo")     // 路由分组(3级路径)
		r2.GET("/abstract", abstract) // 相应请求 /v1/user/showInfo/abstract
		r2.GET("/detail", detail)
	}
	//	路由分组2
	v2 := router.Group("/v2")
	{
		v2.GET("/other", other) // 响应请求 /v2/other
	}
	router.Run(":9090")
}

func other(c *gin.Context) {
	c.JSON(http.StatusOK, ResGroup{"other", c.Request.URL.Path})
}
func login(c *gin.Context) {
	c.JSON(http.StatusOK, ResGroup{"login", c.Request.URL.Path})
}
func detail(c *gin.Context) {
	c.JSON(http.StatusOK, ResGroup{"detail", c.Request.URL.Path})
}
func abstract(c *gin.Context) {
	c.JSON(http.StatusOK, ResGroup{"abstract", c.Request.URL.Path})
}

 

 

 

 

 

 

 

 

 

 

 

 

 

© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
共2条
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片
    • 头像temp mail0
    • 头像Tree Mail0