Lishengxie
  • Posts
  • About
  • Contact
  1. Home
  2. Posts
  3. Gin框架学习

Gin框架学习

Apr 30, 2023 go学习 Lishengxie

概述

Gin框架地址:https://github.com/gin-gonic/gin

参考文档: https://www.kancloud.cn/shuangdeyu/gin_book https://github.com/piexlmax/1010class

Gin服务入门

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() // listen and serve on 0.0.0.0:8080
}

Gin获取Get/Post参数

获取Get参数

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

	// 匹配的url格式:  /welcome?firstname=Jane&lastname=Doe
	router.GET("/welcome", func(c *gin.Context) {
		firstname := c.DefaultQuery("firstname", "Guest") // DefaultQuery可以在参数值不存在时使用传入的默认值
		lastname := c.Query("lastname") // 是 c.Request.URL.Query().Get("lastname") 的简写

		c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
	})
	router.Run(":8080")
}

获取Post参数

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

	router.POST("/form_post", func(c *gin.Context) {
		message := c.PostForm("message")
		nick := c.DefaultPostForm("nick", "anonymous") // 此方法可以设置默认值

		c.JSON(200, gin.H{
			"status":  "posted",
			"message": message,
			"nick":    nick,
		})
	})
	router.Run(":8080")
}

Get和Post参数混合使用

	router := gin.Default()

	router.POST("/post", func(c *gin.Context) {

		id := c.Query("id")
		page := c.DefaultQuery("page", "0")
		name := c.PostForm("name")
		message := c.PostForm("message")

		fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message)
	})
	router.Run(":8080")
}

请求示例:

POST /post?id=1234&page=1 HTTP/1.1
Content-Type: application/x-www-form-urlencoded
name=manu&message=this_is_great

Bind绑定参数&参数验证

为了将请求主体绑定到结构体中,需要使用模型绑定,Gin当前支持JSON、XML、YAML和标准表单值(foo=bar&boo=baz)的绑定。可以使用Must Bind和Should Bind两种绑定方式,推荐使用Should Bind方式

  • Methods: ShouldBind, ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAML, 后几种基于ShouldBind实现相应的行为,可以指定绑定的数据格式,发生错误时会将错误返回供后续处理。下面给出了绑定json格式的示例
// 绑定为json
type Login struct {
	User     string `form:"user" json:"user" xml:"user"  binding:"required"`
	Password string `form:"password" json:"password" xml:"password" binding:"required"`
}
func main() {
	router := gin.Default()

	// Example for binding JSON ({"user": "manu", "password": "123"})
	router.POST("/loginJSON", func(c *gin.Context) {
		var json Login
		if err := c.ShouldBindJSON(&json); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}
		if json.User != "manu" || json.Password != "123" {
			c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
			return
		} 
		c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
	})
	router.Run(":8080")
}

绑定form,uri,xml格式时可以在结构体后的的修饰符中使用相应的字段。

  • 参数验证一个字段用binding:“required"修饰,在绑定时如果该字段的值为空,那么将返回一个错误。可以在binding后使用自定义的验证函数,可以参考文档中提供的自定义验证的示例,需要注意文档中使用的验证器版本是v8,实际使用时版本不一致需要做出更改,可以参考视频教程。

Gin文件上传和返回

文件读取

上传文件的文件名可以由用户自定义,所以可能包含非法字符串,为了安全起见,应该由服务端统一文件名规则 上传文件的示例代码如下所示:

单文件上传

func main() {
	router := gin.Default()
	// 给表单限制上传大小 (默认 32 MiB)
	// router.MaxMultipartMemory = 8 << 20  // 8 MiB
	router.POST("/upload", func(c *gin.Context) {
		// 单文件
		file, _ := c.FormFile("file")
		log.Println(file.Filename)
		// 上传文件到指定的路径
		c.SaveUploadedFile(file, "./saved.txt")
		c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
	})
	router.Run(":8080")
}

使用FormFile接收单文件,使用Postman测试文件上传时,需要先设置Header中Content-Type为multipart/form-data,随后在Body中选择上传form-data类型的键值对,在具体的值中选择文件,同时需要注意Posterman上传文件时工作路径的设置。

多文件上传

主要区别在于先获取文件列表,再依次对每个文件进行处理。

func main() {
	router := gin.Default()
	// 给表单限制上传大小 (默认 32 MiB)
	// router.MaxMultipartMemory = 8 << 20  // 8 MiB
	router.POST("/upload", func(c *gin.Context) {
		// 多文件
		form, _ := c.MultipartForm()
		files := form.File["file"]
		for _, file := range files {
			log.Println(file.Filename)
			// 上传文件到指定的路径
			// c.SaveUploadedFile(file, dst)
		}
		c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))
	})
	router.Run(":8080")
}

文件返回

// Header添加文件相关信息,不加也可以跑?
c.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=%s", file.Filename))
// 在服务器文件路径下启动相应的服务
c.File(dst)

路由分组和中间件

路由分组

对router创建Group就是分组,同一分组中会拥有同一前缀和同一中间件,分组可以使得路由结构更加清晰,更加方便管理路由。路由分组实例如下所示:

func main() {
	router := gin.Default()
	// Simple group: v1
	v1 := router.Group("/v1")
	{
		v1.POST("/login", loginEndpoint)
		v1.POST("/submit", submitEndpoint)
		v1.POST("/read", readEndpoint)
	}
	router.Run(":8080")
}

中间件

在请求到达路由的方法的前和后进行的一系列操作,在路由(路由组)上进行Use操作,后面传入中间件函数即可。

// HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context)

HandlerFunc是一个函数类型,接收一个Context参数。用于编写程序处理函数并返回HandleFunc类型,作为中间件的定义。gin.Default()中默认已经使用了Recovery和Logger两个中间件,无中间件启动服务可以使用gin.New()方法。下面给出了Gin自定义中间件使用的实例

func Logger() gin.HandlerFunc {
	return func(c *gin.Context) {
		t := time.Now()
		// Set example variable
		c.Set("example", "12345")
		
		// before request
		c.Next()
		// after request
		
		latency := time.Since(t)
		log.Print(latency)
		// access the status we are sending
		status := c.Writer.Status()
		log.Println(status)
	}
}

func main() {
	r := gin.New()
	r.Use(Logger())
	r.GET("/test", func(c *gin.Context) {
		example := c.MustGet("example").(string)
		// it would print: "12345"
		log.Println(example)
	})
	// Listen and serve on 0.0.0.0:8080
	r.Run(":8080")
}

中间件中context.Next函数可以将中间件代码的执行顺序一分为二,Next函数调用之前的代码在请求处理之前之前,当程序执行到context.Next时,会中断向下执行,转而先去执行具体的业务逻辑,执行完业务逻辑处理函数之后,程序会再次回到context.Next处,继续执行中间件中后续的代码。

使用Use方法调用自定义的中间件,可以在路由或路由组后调用,需要注意的是,调用多个中间件时可以在一个Use方法中依次写入,也可以连续使用多个Use方法。多个中间件的代码执行顺序可以参考洋葱中间件模型。

Gin日志

可以参考文档相关章节。日志可以用来记录参数信息,猜测用户行为,尝试复现bug并修复,推荐使用第三方的日志工具go-logging,logrus等,对于大量的日志,可以使用go-file-rotatelogs, file-rotatelogs对日志自行按照时间进行切割等。

记录用户的输入输出信息常使用中间件实现。

Gin数据库使用

链接和使用MySQL数据库

import _ "github.com/go-sql-driver/mysql"
connStr := "root:password@tcp(127.0.0.1:3306)/ginsql"
db, err := sql.Open("mysql", connStr)
if err != nil {       log.Fatal(err.Error())
	return
}

ORM进行数据库操作

ORM是一种数据库辅助工具,可以在go结构体和数据库之间产生映射,基于结构体即可完成增删改查操作。可用的ORM框架有GORM,gorose和xorm等。

GORM链接数据库

GORM参考文档如下: https://learnku.com/docs/gorm/v2/index/9728 http://gorm.book.jasperxu.com/ (好像无法访问了) https://gorm.io/zh_CN/docs/connecting_to_the_database.html https://books.studygolang.com/gorm/

  1. 导入gorm
  2. 导入mysql驱动器
  3. 使用Open方法链接得到数据库对象,以MySQL为例
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
// 最后关闭数据库链接
defer db.Close()

加载静态资源&HTML渲染

设置静态文件路径

func main() {
	router := gin.Default()
	router.Static("/assets", "./assets")
	router.StaticFS("/more_static", http.Dir("my_file_system"))
	router.StaticFile("/favicon.ico", "./resources/favicon.ico")
	// Listen and serve on 0.0.0.0:8080
	router.Run(":8080")
}

HTML渲染

func main() {
	router := gin.Default()
	router.LoadHTMLGlob("templates/*")
	//router.LoadHTMLFiles("templates/template1.html", "templates/template2.html")
	router.GET("/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.tmpl", gin.H{
			"title": "Main website",
		})
	})
	router.Run(":8080")
}
<html>
	<h1>
		{{ .title }}
	</h1>
</html>

Table of Contents

  • 概述
  • Gin服务入门
  • Gin获取Get/Post参数
    • 获取Get参数
    • 获取Post参数
    • Get和Post参数混合使用
  • Bind绑定参数&参数验证
  • Gin文件上传和返回
    • 文件读取
  • 路由分组和中间件
    • 路由分组
    • 中间件
  • Gin日志
  • Gin数据库使用
    • 链接和使用MySQL数据库
    • ORM进行数据库操作
    • GORM链接数据库
  • 加载静态资源&HTML渲染
    • 设置静态文件路径
    • HTML渲染

Recent Posts

  • 分布式ID生成方案全解析:从数据库到雪花算法 Jun 25, 2026
  • Let's Encrypt 免费申请 SSL 证书,并实现自动续期 Sep 14, 2025
  • Redis ziplist、quicklist 和 listpack Mar 3, 2025
  • Nginx禁止使用IP直接访问服务器上相应端口 May 11, 2024
  • LeetCode刷题 - KMP算法 Feb 4, 2024

Categories

  • Linux7
  • 算法学习7
  • 论文笔记6
  • C++4
  • 未分类3
  • Go学习2
  • Redis1
  • SystemC1
  • Verilog1

Tags

← Go Gin Chat Verilog Note →

Related Posts

  • Go Gin Chat Apr 30, 2023
皖ICP备2023003716号-1 | 公安备案皖公网安备34012202341113 | 违法和不良信息举报邮箱:1141751053@qq.com
Powered by Hugo & Explore Theme.