【Go语言】Gin 框架教程

Gin 框架教程

1.第一个 Gin 程序

1.1 Gin 安装

shell 复制代码
# 执行执行如下操作即可,安装Gin前需要安装Go环境
go get -u -v github.com/gin-gonic/gin
# -v:打印出被构建的代码包的名字
# -u:已存在相关的代码包,强行更新代码包及其依赖包

1.2 Gin 项目创建

在一个空文件夹里新建文件 main.go,参考如下代码编写一个 Gin 程序。

go 复制代码
// blog.euansu.cn
// main.go
package main

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

func main() {
	r := gin.Default()
	r.GET("/", func(c *gin.Context) {
		c.String(200, "Hello, EuanSu")
	})
	r.Run() // listen and serve on 0.0.0.0:8080
}

运行 main.go 程序,如下图所示。

shell 复制代码
go run main.go

代码说明:

  1. 首先,r := gin.Default()这里生成了一个 Gin 引擎实例,用于处理 HTTP 请求,也即 WSGI 应用程序,这个实例会预先加载一些默认的中间件。
  2. r.GET("/", ...) 则是声明了一个路由,以及路由对应的函数方法。
  3. r.Run() 函数则是运行应用程序,默认的监听端口是 8080,也可以传入参数设置应用程序运行端口,例如 r.Run(":8888"),即应用运行在 8888 端口。

1.3 网站图标设置

这里使用一个 Gin 的中间件 github.com/thinkerou/favicon,使用同 gin 框架,首先是安装 github.com/thinkerou/favicon 库。

shell 复制代码
go get github.com/thinkerou/favicon

接下来,则是使用该中间件,代码如下所示:

go 复制代码
// blog.euansu.cn
// main.go
package main

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

func main() {
	// 创建一个服务
	r := gin.Default()
	// 使用中间件
	r.Use(favicon.New("./static/favicon.ico"))
	// 路由函数
	r.GET("/", func(c *gin.Context) {
		c.String(200, "Hello, EuanSu")
	})
	r.Run() // listen and serve on 0.0.0.0:8080
}

项目运行后,就能够在浏览器中看到 favicon.ico 网站图标。

2.Gin 中的路由

2.1 路由语法

go 复制代码
$router.$method("$router", $handlerFunction)
# router,Gin 引擎实例
# method,http路由方法,可选参数为:GET、POST、PUT、PATCH、DELETE、OPTIONS、HEAD以及能够处理任意类型的HTTP请求的Any和处理指定类型的HTTP请求的Match
# handlerFunction,路由函数,处理路由响应

示例如下:

go 复制代码
router.GET("/path", handlerFunction) 								// 获取资源
router.POST("/path", handlerFunction) 								// 创建资源	
router.PUT("/path", handlerFunction) 								// 更新资源
router.DELETE("/path", handlerFunction) 							// 删除资源
router.PATCH("/path", handlerFunction) 								// 更新部分资源
router.OPTIONS("/path", handlerFunction) 							// 获取服务器支持的 HTTP 方法
router.HEAD("/path", handlerFunction) 								// 获取资源的头部信息
router.Any("/path", handlerFunction) 								// 处理任意类型的 HTTP 请求
router.Match([]string{"GET", "POST"}, "/path", handlerFunction) 	// 处理指定类型的 HTTP 请求

2.2 请求参数的处理

2.2.1 路由参数解析

参数包含在路由中,如 /user/:name,通过调用不同的 路由参数 传入不同的 name,如下所示:

go 复制代码
r.GET("/user/:name", func(c *gin.Context) {
    name := c.Param("name")
    c.String(http.StatusOK, "%s, Welcome to your clicl.", name)
})

这里需要注意下,如果新增了路由,需要重启 Gin 程序,重启之后,再次访问路由地址,就能够正常解析路由参数了。

2.2.2 Query 参数解析

Query 参数 与上文的路由参数一样,参数都是路由的一部分,不同的是 Query 参数 是键值对的形式,查询参数通常位于URL的问号(?)之后,以键值对的形式出现,并且多个参数之间用与号(&)分隔。

go 复制代码
r.GET("/users", func(c *gin.Context) {
    name := c.Query("name")
    c.String(http.StatusOK, "%s, Thank you for your click.", name)
})

也可以直接使用 URL 通过浏览器请求该接口。

2.2.3 POST 参数解析(form表单)

这里使用一个表单参数进行举例,如下所示,输入分数转化为及格、不及格的判断,代码如下所示。

go 复制代码
package main

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

func evaluateScore(score int) string {
	if score >= 90 {
		return "优秀"
	} else if score >= 60 {
		return "及格"
	} else {
		return "不及格"
	}
}

func main() {
	r := gin.Default()
	// POST参数解析
	// POST
	r.POST("/form", func(c *gin.Context) {
		username := c.PostForm("username")
        // DefaultPostForm,只支持string类型,因此需要进行类型的转换
		scoreStr := c.DefaultPostForm("score", "0")
		score, err := strconv.Atoi(scoreStr)
		if err != nil {
			// 处理错误情况,例如当输入的值不是整数时
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid score"})
			return
		}

		result := evaluateScore(score)
		c.JSON(http.StatusOK, gin.H{"username": username, "score": score, "evaluation": result})
	})

	r.Run() // listen and serve on 0.0.0.0:8080
}
r.POST("/form", func(c *gin.Context) {
    username := c.PostForm("username")
    password := c.DefaultPostForm("password", "000000") // 可设置默认值
    fmt.Println(password)
    c.String(http.StatusOK, "%s, Thank you for your login.", username)
})

请求后端的接口,返回如下。

2.2.4 POST 参数解析(json)
go 复制代码
package main

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

func evaluateScore(score int) string {
	if score >= 90 {
		return "优秀"
	} else if score >= 60 {
		return "及格"
	} else {
		return "不及格"
	}
}

// 定义结构体,与JSON数据匹配
type Person struct {
	Username string `json:"username"`
	Score    int    `json:"score"`
}

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

	// 处理POST请求,接收JSON参数
	r.POST("/json", func(c *gin.Context) {
		var person Person

		// 绑定JSON到结构体
		if err := c.ShouldBindJSON(&person); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		result := evaluateScore(person.Score)
		c.JSON(http.StatusOK, gin.H{"username": person.Username, "score": person.Score, "evaluation": result})
	})

	r.Run() // listen and serve on 0.0.0.0:8080
}

请求后端的接口,返回如下。

2.3 路由处理

2.3.1 重定向
go 复制代码
r.GET("/redirect", func(c *gin.Context) {
    c.Redirect(http.StatusMovedPermanently, "/")
})

如下所示,当后端收到 /redirect 路由的请求,会重定向至 /

2.3.2 分组路由

分组路由主要是为了处理路由前缀一致的情况,例如有一组路由前缀都是 /api/v1 开头,通过分组路由就能够简化路由的定义,也可以更好的实现路由的权限控制,例如将需要登录的路由放到同一分组中。代码示例如下:

go 复制代码
// 默认的路由方法
defaultHandler := func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{
        "path": c.FullPath(),
    })
}

// group: v1
v1 := r.Group("/v1")
{
    v1.GET("/posts", defaultHandler)
    v1.GET("/series", defaultHandler)
}
// group: v2
v2 := r.Group("/v2")
{
    v2.GET("/posts", defaultHandler)
    v2.GET("/series", defaultHandler)
}

请求不同的路由地址,均能够得到正常的响应。

3.RESTful API

RESTful API 具体概念可以查看 https://blog.euansu.cn/post/djangorestframework/ 这篇文章中关于 RESTful API 的相关介绍 。

使用 Go 语言能够快速的实现 RESTful API,实现如下:

go 复制代码
r.GET("/user", func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{"msg": "get user"})
})
r.POST("/user", func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{"msg": "post user"})
})
r.PUT("/user", func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{"msg": "put user"})
})
r.DELETE("/user", func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{"msg": "delete user"})
})

使用 Apifox 等接口测试工具测试,应用程序能够根据使用的 HTTP 请求方式的不同而使用不同的函数进行处理。

  • GET 请求

  • POST 请求

  • PUT 请求

  • DELETE 请求

如上测试所示,Gin 框架能够快速、简洁的实现 RESTful API

4.响应页面

可以通过以下方式,先加载静态页面到 Gin 应用程序中。

go 复制代码
// 加载静态页面
r.LoadHTMLGlob("templates/*")
// 加载指定的静态页面(不推荐)
r.LoadHTMLFiles("templates/index.html")

路由函数

go 复制代码
r.GET("/", func(c *gin.Context) {
    c.HTML(http.StatusOK, "index.html", gin.H{"msg": "index.html", "path": "/"})
})

打开浏览器,访问 Gin/ 路由,显示如下。

5.中间件

首先是在项目中,声明一个中间件方法 myHandler(),如下是 myHandler() 的代码方法。

go 复制代码
func myHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 设置中间件的值
		c.Set("usersession", "xxx")
		if c.Request.URL.Path == "/" {
			// 阻止
			fmt.Println("阻止")
			c.Abort()
		}
		// 放行
		fmt.Println("放行")
		c.Next()
	}
}

Gin 应用程序中使用这个中间件。

go 复制代码
func main() {
	// 创建一个服务
	r := gin.Default()
	// 使用中间件
	r.Use(myHandler())
	...
    
	r.Run() // listen and serve on 0.0.0.0:8080
}

实际测试效果如下:

  • 请求 / 路由,后端返回为空。

  • 请求其他的路由,则不受影响,就如我们在中间件方法所写的判断一样,仅仅只是拦截了 / 的路由请求。

6.数据库

Gin 项目中使用数据库,涉及以下操作:

  1. 安装数据库驱动和 ORM 库,安详需要使用的数据库驱动和 ORM 库,常见的 ORM 库是 GORM,支持 MySQLPostgreSQLSQLite 等数据库。
  2. 配置数据库的连接信息,需要在 Gin 项目配置中配置数据库连接,通常是 main.go 或者单独的配置文件。
  3. 初始化数据库,在项目启动的时候,进行数据库连接的初始化。
  4. 项目中使用数据库, 在路由关联的函数中使用数据进行增删改查的操作。

6.1 安装数据库驱动和 ORM 库

shell 复制代码
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql

6.2 配置数据库的连接信息

main.go 文件中配置数据库的连接信息。

go 复制代码
// 数据库连接信息
dsn := "username:password@tcp(127.0.0.1:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
var err error

// 连接数据库
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
    log.Fatalf("无法连接数据库: %v", err)
}

6.3 数据库初始化

使用 gorm 库,定义数据库模型,在 main.go 文件中添加 AutoMigrate 方法,进行数据库的迁移操作。

go 复制代码
// 定义一个全局的数据库连接变量
var db *gorm.DB

// User 定义数据库模型
type User struct {
	ID       uint   `json:"id" gorm:"primaryKey"`
	Username string `json:"username"`
	Email    string `json:"email"`
}

func main() {
	// 创建一个服务
	r := gin.Default()
	// 使用中间件
	r.Use(favicon.New("./static/favicon.ico"))
	r.Use(myHandler())
	// 加载静态页面
	r.LoadHTMLGlob("templates/*")
	// 数据库连接信息
	dsn := "username:password@tcp(127.0.0.1:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
	var err error

	// 连接数据库
	db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		log.Fatalf("无法连接数据库: %v", err)
	}

	// 自动迁移数据库
	db.AutoMigrate(&User{})

	r.Run() // listen and serve on 0.0.0.0:8080
}

项目启动如下,项目启动后,执行 AutoMigrate 方法。

连接配置的数据库,发现 AutoMigrate 初始化生成的表,数据库迁移操作成功。

6.4 数据库使用

如下是获取用户列表和创建用户的两个函数方法。

go 复制代码
// 获取用户列表的处理函数
func getUsers(c *gin.Context) {
	var users []User
	result := db.Find(&users)
	if result.Error != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()})
		return
	}
	c.JSON(http.StatusOK, users)
}

// 创建用户的处理函数
func createUser(c *gin.Context) {
	var user User
	if err := c.ShouldBindJSON(&user); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	result := db.Create(&user)
	if result.Error != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()})
		return
	}
	c.JSON(http.StatusOK, user)
}

main.go 中添加路由函数,如下所示:

go 复制代码
// 定义路由和处理函数
r.GET("/users", getUsers)
r.POST("/users", createUser)

进行路由请求,如下所示:

  • POST 请求进行用户的创建。

  • GET 请求获取用户信息。

数据库操作的完整代码为:

go 复制代码
// blog.euansu.cn
// main.go
package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/thinkerou/favicon"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"log"
	"net/http"
)

func myHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 设置中间件的值
		c.Set("usersession", "xxx")
		if c.Request.URL.Path == "/" {
			// 阻止
			fmt.Println("阻止")
			c.Abort()
		}
		// 放行
		fmt.Println("放行")
		c.Next()
	}
}

// 定义一个全局的数据库连接变量
var db *gorm.DB

// User 定义数据库模型
type User struct {
	ID       uint   `json:"id" gorm:"primaryKey"`
	Username string `json:"username"`
	Email    string `json:"email"`
}

func main() {
	// 创建一个服务
	r := gin.Default()
	// 使用中间件
	r.Use(favicon.New("./static/favicon.ico"))
	r.Use(myHandler())
	// 加载静态页面
	r.LoadHTMLGlob("templates/*")
	// 数据库连接信息
	dsn := "username:password@tcp(127.0.0.1:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
	var err error

	// 连接数据库
	db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		log.Fatalf("无法连接数据库: %v", err)
	}

	// 自动迁移数据库
	db.AutoMigrate(&User{})

	// 定义路由和处理函数
	r.GET("/users", getUsers)
	r.POST("/users", createUser)

	// 路由函数
	r.GET("/", func(c *gin.Context) {
		//c.String(200, "Hello, EuanSu")
		c.HTML(http.StatusOK, "index.html", gin.H{"msg": "index.html", "path": "/"})
	})

	r.GET("/home", func(c *gin.Context) {
		//c.String(200, "Hello, EuanSu")
		c.HTML(http.StatusOK, "index.html", gin.H{"msg": "index.html", "path": "/home"})
	})

	r.Run() // listen and serve on 0.0.0.0:8080
}

func getUsers(c *gin.Context) {
	var users []User
	result := db.Find(&users)
	if result.Error != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()})
		return
	}
	c.JSON(http.StatusOK, users)
}

func createUser(c *gin.Context) {
	var user User
	if err := c.ShouldBindJSON(&user); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	result := db.Create(&user)
	if result.Error != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()})
		return
	}
	c.JSON(http.StatusOK, user)
}

7.相关链接

[1] Go Gin 简明教程 https://geektutu.com/post/quick-go-gin.html

[2] 【【狂神说】Gin框架一小时上手 | 快速转型GoWeb开发 | Go语言零基础教程】 https://www.bilibili.com/video/BV1Rd4y1C7A1/?share_source=copy_web\&vd_source=5fdcc6213ac2d30f16a78fe5d6e8df4d

相关推荐
加油,旭杏6 小时前
【go语言】map 和 list
开发语言·golang·list
Amd79414 小时前
深入探讨数据库索引类型:B-tree、Hash、GIN与GiST的对比与应用
数据结构·gin·b-tree·查询优化·数据库索引·gist·hash索引
薛语澄18 小时前
CSS语言的网络编程
开发语言·后端·golang
川Princess19 小时前
【后端开发】字节跳动青训营之Go语言进阶与依赖管理
开发语言·后端·golang
薛语澄20 小时前
Scala语言的移动应用开发
开发语言·后端·golang
SyntaxSage20 小时前
Elixir语言的数据结构
开发语言·后端·golang
萧澄华21 小时前
Powershell语言的云计算
开发语言·后端·golang
寻找优秀的自己21 小时前
Gin 应用并注册 pprof
gin·性能调优·pprof
梦想画家21 小时前
Golang Gin系列-5:数据模型和数据库
数据库·golang·gin
朗迹 - 张伟21 小时前
Gin 学习笔记
笔记·学习·gin