Gin 使用
下载并安装 gin
go get -u github.com/gin-gonic/gin
由于网络原因国内部分用户可能没法直接下载第三方包,go get 失败,Golang Gin中没法下载第三方包解决办法如下:
//打开终端并执行
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
如果使用诸如 http.StatusOK 之类的常量,则需要引入 net/http 包
net/http //go自带基础包
案例
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
// 创建gin的默认路由引擎
r := gin.Default()
// 配置路由
r.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{ // c.JSON:返回 JSON 格式的数据
"message": "Hello world!"})
})
// 浏览器访问请求127.0.0.1:8080/ping路由时,调用回调函数
r.GET("/ping", func(c *gin.Context) {
//浏览器输出
//c.JSON(200, gin.H{
// "message": "pong",
//})
c.String(http.StatusOK, "值:%v", "你好")
})
//监听并在 0.0.0.0:8080 上启动服务(启动一个web服务)
r.Run()
// 监听并在 0.0.0.0:8000 上启动服务(启动一个web服务)
//r.Run("8000")
}
go gin程序的热加载
Go Gin 程序的热加载是指在开发过程中,当您修改了源代码后,无需手动停止并重新启动应用程序,而是让框架自动检测代码变更并重新编译、加载新的代码版本,从而立即反映到正在运行的服务中。这对于提高开发效率、缩短反馈循环非常有帮助。虽然 Gin 框架本身并未内置热加载功能,但可以通过以下第三方工具实现这一特性:
1、Fresh
Fresh 是一个常用的 Go 应用程序热加载工具,由 Pilu Ristea 开发。要使用 Fresh 实现 Gin 程序的热加载,可以按照以下步骤操作:
安装 Fresh:
go install github.com/pilu/fresh@latest
启动应用: 在项目根目录下运行 fresh
命令代替直接执行 go run main.go
或 go build && ./myapp
:
fresh
Fresh 将监听项目中的源文件变化,一旦检测到改动,会自动重新编译并重启应用程序。
结束程序
Ctrl + C
2. codegangsta
//进入终端执行
go get -u github.com/codegangsta/gin
//然后运行命令
gin run main.go
注意事项
- 使用热加载工具时,请确保您的开发环境已正确设置 Go 的模块支持(如设置
GO111MODULE=on
环境变量),特别是在使用 Go modules 进行依赖管理的情况下。 - 对于生产环境部署,不建议使用热加载功能,应通过正常的构建和部署流程更新应用,以确保稳定性和一致性。
通过集成第三方工具可以实现 Go Gin 程序的热加载,显著提升开发过程中的迭代速度和工作效率。选择适合自己项目的工具,按照对应的文档或上述指导步骤进行配置和使用即可。
Gin框架中的路由系统是其核心功能之一,负责将接收到的HTTP请求映射到相应的处理函数。以下是对Gin框架路由特性和使用方法的详细说明:
路由结构与数据结构
前缀树(Trie/Radix Tree)
Gin框架底层使用了基于前缀树(Trie或Radix Tree)的数据结构来高效存储和查找路由。这种数据结构非常适合处理HTTP路径,因为它允许以接近O(1)的时间复杂度查找具有共同前缀的路径。这意味着即使面对大量路由规则,Gin也能快速定位到正确的处理函数,从而保证了高并发场景下的性能。
O(1):意味着Gin框架在处理路由查找时,尽管理论上的时间复杂度与路径长度有关,但在实际应用中,由于路径长度较小且固定,查找过程的时间开销几乎不受路由集合大小的影响,表现得如同常数时间复杂度算法一样高效。这种特性使得Gin框架在处理高并发请求时,能够迅速定位到对应的处理函数,保证了系统的响应速度和吞吐量
路由定义
在Gin中,开发者使用简洁的API来定义路由及其关联的HTTP方法、路径和处理函数。例如:
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
// 定义GET请求路由
r.GET("/users/:id", getUser)
// 定义POST请求路由
r.POST("/users", createUser)
// 启动服务器
r.Run(":8080")
}
// 处理函数示例
func getUser(c *gin.Context) {
id := c.Param("id")
// ...
}
func createUser(c *gin.Context) {
// ...
}
在这个例子中:
GET
和POST
方法分别用于定义处理相应HTTP方法的路由。- 路径
/users/:id
中的:id
是一个参数占位符,表示该位置可以接收任意字符串作为参数,通过c.Param("id")
在处理函数中访问。 createUser
和getUser
分别是与这些路由关联的处理函数,它们接受一个*gin.Context
参数,包含了请求上下文的所有相关信息。
路由特性
静态路由与动态路由
-
静态路由:指路径中不含参数占位符的路由,如
/about
。这类路由匹配的是完全相同的URL路径。 -
动态路由:指路径中含有参数占位符的路由,如
/users/:id
。这些占位符可以捕获URL中的特定部分作为参数传递给处理函数。
路由组
为了更好地组织和复用路由,Gin支持创建路由组。路由组可以共享中间件、设置共同的前缀,并保持代码的模块化。创建路由组的示例如下:
v1 := r.Group("/api/v1")
{
v1.GET("/users", listUsers)
v1.GET("/users/:id", getUserDetails)
v1.POST("/users", createUser)
// 更多v1组内的路由...
}
在这个例子中,所有定义在 v1
组内的路由都将带有前缀 /api/v1
。
路由参数
除了简单的参数占位符,Gin还支持更复杂的路由参数约束和命名参数:
-
参数约束:通过在参数名后添加冒号和正则表达式来限制参数值的格式,如
:id([0-9]+)
表示id
参数只能包含数字。 -
命名参数:在
GET
请求的查询参数中,可以直接通过c.Query("paramName")
访问;在POST
请求的表单数据或JSON请求体中,可通过c.PostForm("paramName")
或c.ShouldBindJSON(&struct{})
解析。
中间件与路由
路由定义不仅可以关联处理函数,还可以附加中间件。中间件是在请求到达实际处理函数之前或之后执行的函数,常用于身份验证、日志记录、响应压缩、跨域处理等通用任务。中间件可以全局应用、应用于特定路由组,也可以仅作用于单个路由。
r := gin.Default()
// 全局中间件示例
r.Use(gin.Logger(), gin.Recovery())
// 路由组中间件示例
authMiddleware := func(c *gin.Context) {
// ...身份验证逻辑...
}
protected := r.Group("/", authMiddleware)
{
protected.GET("/private", handlePrivateResource)
}
// 单个路由中间件示例,someMiddleware为够个中间件,slowHandler为处理函数
r.GET("/slow-resource", someMiddleware, slowHandler)
总结
Gin框架中的路由系统以其高效的前缀树数据结构、简洁的API设计、支持静态与动态路由、路由组、参数约束、中间件集成等特性,为开发者提供了强大且灵活的路由管理能力。这些特性使得构建清晰、高性能的RESTful API和Web应用程序变得轻松高效。
代码案例
GET请求
返回一个字符串
// GET 请求
r.GET("网址", func(c *gin.Context) {
c.String(200, "Get")
})
// 域名/news?aid=20
// 返回一个字符串
r.GET("/news", func(c *gin.Context) {
aid := c.Query("aid")
c.String(200, "aid=%s", aid)
})
// 域名/user/20
r.GET("/user/:uid", func(c *gin.Context) {
uid := c.Param("uid")
c.String(200, "userID=%s", uid)
})
GET请求返回一个 JSON 数据
//方法一:自己拼接JSON
r.GET("/json", func(c *gin.Context) {
//返回json数据,使用 map[string]interface
//c.JSON(返回的状态码, 任意类型的数据(如:map,struct,...)
c.JSON(200, map[string]interface{}{
"success": true,
"msg": "你好",
})
})
//方法二:gin中的H函数
r.GET("/json2", func(c *gin.Context) {
//返回json数据,使用gin中的H函数, gin.H 是 map[string]interface{}的缩写
c.JSON(200, gin.H{
"success": true,
"msg": "你好gin",
})
})
//方法三:使用结构体
r.GET("/json3", func(c *gin.Context) {
//实例化一个结构体
a := &Article{
Title: "标题",
Desc: "说明",
Content: "内容",
}
c.JSON(200, a)
})
GET请求返回 XML 数据
//方法一:使用gin.H返回
r.GET("/xml", func(c *gin.Context) {
c.XML(http.StatusOK, gin.H{
"success": true,
"msg": "成功xml",
})
})
//方法二:使用结构体
r.GET("/xmlStruct", func(c *gin.Context) {
//实例化一个结构体
a := &Article{
Title: "标题-xmlStruct",
Desc: "说明-xmlStruct",
Content: "内容-xmlStruct",
}
c.XML(200, a)
})
GET请求返回HTML数据
//初始化路由
r := gin.Default()
//加载templates文件中所有模板文件,以便后续c.HTML()渲染文件时使用
r.LoadHTMLGlob("templates/*")
r.GET("/news", func(c *gin.Context) {
//使用模板文件渲染HTML文件
//前提: r.LoadHTMLGlob("templates/*")
//HTML(状态码, 要渲染的文件名, 加载的参数)
c.HTML(http.StatusOK, "news.html", gin.H{
"title": "我是一个news",
})
})
POST请求
// POST请求
r.POST("网址", func(c *gin.Context) {
c.String(200, "POST")
})
PUT请求
// PUT请求
r.PUT("网址", func(c *gin.Context) {
c.String(200, "PUT")
})
DELETE请求
//DELETE请求
r.DELETE("网址", func(c *gin.Context) {
c.String(200, "DELETE")
})
完整代码案例一
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default() // 创建gin的默认路由引擎
//配置路由
// 浏览器访问请求127.0.0.1:8080/ping路由时,调用回调函数
r.GET("/ping", func(c *gin.Context) {
//浏览器输出
//c.JSON(200, gin.H{
// "message": "pong",
//})
c.String(http.StatusOK, "值:%v", "你好")
})
r.GET("/news", func(c *gin.Context) {
//使用http.StatusOK状态码
c.String(http.StatusOK, "新闻页11面")
})
r.POST("/add", func(c *gin.Context) {
c.String(200, "这是一个POST请求,主要用于增加数据")
})
r.PUT("/edit", func(c *gin.Context) {
c.String(200, "这是一个PUT请求,主要用于修改数据")
})
r.DELETE("/delete", func(c *gin.Context) {
c.String(200, "这是一个DELETE请求,主要用于删除数据")
})
r.Run() // 监听并在 0.0.0.0:8080 上启动服务(启动一个web服务)
}
完整代码案例二
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
type Article struct {
Title string `json:"title"`
Desc string `json:"desc"`
Content string `json:"content"`
}
func main() {
//初始化路由
r := gin.Default()
//加载templates文件中所有模板文件,以便后续c.HTML()渲染文件时使用
r.LoadHTMLGlob("templates/*")
//配置路由
r.GET("/", func(c *gin.Context) {
c.String(200, "首页")
})
r.GET("/json", func(c *gin.Context) {
//返回json数据,使用 map[string]interface
//c.JSON(返回的状态码, 任意类型的数据(如:map,struct,...)
c.JSON(200, map[string]interface{}{
"success": true,
"msg": "你好",
})
})
r.GET("/json2", func(c *gin.Context) {
//返回json数据,使用gin中的H函数
c.JSON(200, gin.H{
"success": true,
"msg": "你好gin",
})
})
r.GET("/json3", func(c *gin.Context) {
//实例化一个结构体
a := &Article{
Title: "标题",
Desc: "说明",
Content: "内容",
}
c.JSON(200, a)
})
//jsonp请求 主要用来解决跨域问题
//http://127.0.0.1:8080/jsonp?callback=call
//call({"title":"标题-jsonp","desc":"说明-jsonp","content":"内容-jsonp"});
r.GET("/jsonp", func(c *gin.Context) {
//实例化一个结构体
a := &Article{
Title: "标题-jsonp",
Desc: "说明-jsonp",
Content: "内容-jsonp",
}
c.JSONP(200, a)
})
r.GET("/xml", func(c *gin.Context) {
c.XML(http.StatusOK, gin.H{
"success": true,
"msg": "成功xml",
})
})
r.GET("/news", func(c *gin.Context) {
//使用模板文件渲染HTML文件
//前提: r.LoadHTMLGlob("templates/*")
//HTML(状态码, 要渲染的文件名, 加载的参数)
c.HTML(http.StatusOK, "news.html", gin.H{
"title": "我是一个news",
})
})
r.GET("/goods", func(c *gin.Context) {
//使用模板文件渲染HTML文件
//前提: r.LoadHTMLGlob("templates/*")
//HTML(状态码, 要渲染的文件名, 加载的参数)
c.HTML(http.StatusOK, "goods.html", gin.H{
"title": "我是一个goods",
"price": 12.99,
})
})
r.Run() // 启动一个web服务
}