1. Gin框架简介
1.1 Gin是什么?
Gin 是一个用 Go (Golang) 编写的 Web 框架。 它具有类似 martini 的 API,但是性能要好得多,由于采用的路由库是基于 httprouter做的,速度提高了 40 倍。
1.2 为什么要使用Gin?
直接使用标准库开发Web服务可以实现简单的功能,但在实际开发中会遇到一些限制和不便之处。例如:
- 路由注册能力有限:标准库提供的路由功能只支持精确匹配,无法处理通配符、路径参数等场景。而Gin框架提供了更强大的路由功能,可以通过通配符和路径参数等方式灵活定义路由规则,满足更多实际开发需求。
- 请求和响应处理繁琐:标准库暴露给开发者的函数参数需要直接从请求中读取数据、反序列化,响应时手动序列化、设置Content-Type、写响应内容,这些操作相对繁琐。而Gin框架封装了这些操作,提供了简洁的API,使请求和响应处理更加方便和高效。
- 业务和非业务代码耦合:直接基于标准库开发时,业务和非业务代码不可避免地会耦合在一起,导致代码可读性和可维护性下降。而Gin框架提供了中间件机制,可以在不侵入业务代码的情况下,对请求和响应进行前置或后置处理,使业务代码与非业务代码分离,提高代码的可读性和可维护性。
虽然标准库可以实现简单的Web服务,但在实际开发中,为了满足更多需求并提高开发效率和代码质量,使用Gin框架是一个更好的选择。
1.3 Gin 优点
- 较轻量级,事实上是加了路由和模板功能的net/http库
- 第三方组件兼容性好,可以自由选择第三方组件
1.4 Gin 缺点
- 过于轻量级,初学者初期需要花时间去寻找学习第三方组件的使用方式,并将其融合到项目中
- 官方文档较简单
- 没有工程化的组件,需要自行研究引入。
1.5 下载安装 gin 包
go get -u github.com/gin-gonic/gin
1.6 一个简单的例子
通过上面的了解,相信你已经对Gin框架产生了浓厚的兴趣,那么接下来让我们看看一个简单的例子:
- 首先新建一个项目(gintest),然后在项目的根目录下新建一个go文件(hello_world.go)代码如下:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
router := gin.Default()
router.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Hello World")
})
router.Run(":8000")
}
-
代码部分完成了,然后在控制台安装包
go get -u github.com/gin-gonic/gin,运行程序go run hello_world.go -
打开浏览器并访问指定的端口:http://127.0.0.1:8000/
通过以上代码,我们使用Gin框架创建一个HTTP服务器,并定义一个路由规则。当用户访问根路径"/"时,服务器会返回一个"Hello World"的字符串作为响应。让我们分析一下上面的代码:
-
导入所需的包:
github.com/gin-gonic/gin:Gin框架的主要包,用于创建路由引擎和处理HTTP请求。net/http:Go语言的标准库,用于处理HTTP请求和响应。
-
main函数:- 程序的入口函数。
- 创建一个Gin路由引擎实例,使用
gin.Default()方法创建一个默认配置的引擎。
-
路由定义:
- 使用
router.GET()方法定义一个GET请求的路由规则。 - 路由规则中的第一个参数是要匹配的路径,这里是根路径"/"。
- 第二个参数是一个处理函数,当请求匹配到该路由规则时,会执行这个函数。
- 使用
-
处理函数:
- 匿名函数
func(c *gin.Context) { ... }作为处理函数。 - 处理函数的参数是一个
gin.Context类型的指针,用于处理请求和生成响应。 - 在处理函数中,使用
c.String()方法将字符串"Hello World"作为响应内容发送给客户端。 http.StatusOK是一个HTTP状态码,表示请求成功。
- 匿名函数
-
启动服务器:
- 使用
router.Run(":8000")方法启动HTTP服务器,并监听在8000端口上。 - 这样,当有请求到达服务器的根路径时,会执行定义的处理函数并返回响应。
- 使用
2. Gin框架的路由
2.1 启动服务器
2.1.1 Run()方法启动
router.Run()
这种方式是使用Gin框架提供的Run()方法来启动服务器。它默认监听在localhost:8080地址上,是一种简便的启动方式。
2.1.2 http自定义启动
除了使用router.Run()的方式默认启动外,还可以用http.ListenAndServe()方法启动,比如
func main() {
router := gin.Default()
http.ListenAndServe(":8080", router)
}
或者自定义HTTP服务器的配置:
func main() {
router := gin.Default()
s := &http.Server{
Addr: ":8080",
Handler: router,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
s.ListenAndServe()
}
这种方式是使用Go语言的标准库中的http.ListenAndServe()方法来启动服务器,同时将路由引擎实例作为参数传递给该方法。通过这种方式,你可以更加灵活地配置服务器的地址和其他参数。
2.1.3 区别与联系
-
区别:
router.Run()是 Gin 框架提供的方法,它使用了默认的http.Server配置,监听在localhost:8080上。http.ListenAndServe()是 Go 语言标准库中的方法,它接受一个地址和一个处理器作为参数,可以更加灵活地配置服务器的地址和其他参数。
-
联系:
- 通过查看Run函数的源代码,我们可以发现
router.Run()实际上内部调用了http.ListenAndServe(),并传递了 Gin 框架的路由引擎实例作为处理器。这两种方法本质是一样的。 -
// Run方法源代码 func (engine *Engine) Run(addr ...string) (err error) { defer func() { debugPrintError(err) }() if engine.isUnsafeTrustedProxies() { debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" + "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.") } address := resolveAddress(addr) debugPrint("Listening and serving HTTP on %s\n", address) err = http.ListenAndServe(address, engine.Handler()) return }
- 通过查看Run函数的源代码,我们可以发现
总而言之,第一种代码是使用Gin框架提供的简便方式启动服务器,而第二种代码是使用Go语言的标准库来启动服务器,并具有更多的配置选项。你可以根据需要选择适合的方式来启动你的Gin应用程序。
2.2 路由
2.2.1 web路由的基本概念
路由是Web应用中的一个重要概念,它决定了如何将客户端的请求映射到相应的处理程序上。简单来说,路由是将URL与处理逻辑进行关联的过程。
在Web应用中,客户端通过发送HTTP请求来与服务器进行交互。每个HTTP请求都包含一个URL,用于标识客户端要访问的资源。而路由则决定了当收到这个URL请求时,应该调用哪个处理程序来处理该请求。
为什么需要路由呢?主要有以下几个原因:
- 动态资源映射: Web应用中的资源通常是动态生成的,而不是静态的文件。通过路由,可以将请求的URL与相应的处理程序绑定,从而动态地生成所需的资源。
- 组织和管理请求: Web应用可能包含多个页面和功能,路由可以帮助我们将不同的URL请求按照业务逻辑进行组织和管理。通过定义不同的路由规则,可以将请求分发到不同的处理程序中,实现代码的模块化和可维护性。
- URL参数解析: 在Web应用中,URL通常携带一些参数,如查询参数、路径参数等。通过路由,可以方便地从URL中提取这些参数,并将其传递给相应的处理程序进行处理。
- 中间件支持: 路由也提供了中间件的支持,中间件可以在请求到达处理程序之前或之后执行一些公共的逻辑。通过路由,我们可以注册和使用中间件,实现日志记录、身份验证、错误处理等功能。
总而言之,路由在Web应用中发挥着重要的作用,它决定了客户端请求的流向,将请求与相应的处理程序进行关联。通过合理使用路由,可以使Web应用更加灵活、模块化和易于管理。
2.2.2 gin框架中的路由引擎
Gin路由引擎的主要作用是处理HTTP请求和路由分发。它负责将接收到的HTTP请求与相应的处理器函数进行匹配,并将请求交给匹配到的处理器函数进行处理。在gin框架中有两种方法创建。
-
创建默认配置的路由引擎实例
router := gin.Default()通过查看
gin.Default()方法的源代码,我们可以知道gin.Defaut()方法是对gin.New()方法的封装,但是其中加入了Logger, Recovery这两个中间件,这些中间件可以记录请求日志和处理panic,提供了一些基本的功能和保护措施。 -
创建空白路由引擎实例
router := New()使用
gin.New()方法可以创建一个空白的路由引擎实例。空白实例没有默认配置中间件,需要根据需要手动添加。
2.2.3 路由注册
当我们在Gin框架中注册路由时,可以使用路由引擎实例的各种HTTP方法来定义路由规则。这些HTTP方法对应了不同的请求类型,如GET、POST、PUT、DELETE等。通过注册路由规则,我们可以将不同的URL路径和HTTP方法映射到相应的处理器函数上。
下面我们来详细解读一下路由注册的方式:
-
使用
GET方法注册GET请求的路由:router.GET("/path", handlerFunc)上述代码中,使用
GET方法定义了一个GET请求的路由规则。当收到匹配的GET请求时,Gin路由引擎会调用handlerFunc作为处理器函数来处理请求。 -
使用
POST方法注册POST请求的路由:router.POST("/path", handlerFunc)类似地,使用
POST方法定义了一个POST请求的路由规则。当收到匹配的POST请求时,Gin路由引擎会调用handlerFunc来处理请求。 -
使用
PUT方法注册PUT请求的路由:router.PUT("/path", handlerFunc)使用
PUT方法定义了一个PUT请求的路由规则。当收到匹配的PUT请求时,Gin路由引擎会调用handlerFunc来处理请求。 -
使用
DELETE方法注册DELETE请求的路由:router.DELETE("/path", handlerFunc)使用
DELETE方法定义了一个DELETE请求的路由规则。当收到匹配的DELETE请求时,Gin路由引擎会调用handlerFunc来处理请求。
除了上述常用的HTTP方法,Gin还提供了其他HTTP方法的路由注册方式。比如,使用PATCH方法注册PATCH请求的路由,使用HEAD方法注册HEAD请求的路由,使用OPTIONS方法注册OPTIONS请求的 路由等。这些方法的使用方式类似,只需要替换对应的HTTP方法即可。
另外,Gin还提供了Any方法来注册可以匹配任意HTTP方法的路由。使用Any方法时,需要传入一个路由路径和处理器函数。Gin会将该路由路径与所有HTTP方法进行匹配,从而可以对任意HTTP方法的请求进行处理。
router.Any("/path", handlerFunc)
通过以上方式,我们可以在Gin框架中使用不同的HTTP方法来注册路由规则,根据不同的请求类型来调用相应的处理器函数。这样可以实现灵活的路由处理和请求分发。由此可见通过Gin框架,我们可以轻松地采用RESTful风格的软件架构。
2.2.4 路由匹配
路由匹配是指在Gin框架中,将收到的HTTP请求与路由规则进行匹配的过程。当有请求到达时,Gin路由引擎会根据请求的URL路径和HTTP方法,找到与之匹配的路由规则,并将请求分发给对应的处理器函数进行处理。值得注意的是Gin不支持正则匹配,需要自实现。
Gin框架提供了多种路由匹配的方式,常用的方式包括:
- 精确匹配:URL路径必须与路由规则完全匹配。
- 参数匹配:URL路径中的某些部分可以作为参数进行匹配。
- 通配符匹配:URL路径可以匹配任意的字符或者一定范围的字符。
以下是一些示例:
-
精确匹配:
router.GET("/users", func(c *gin.Context) { c.JSON(http.StatusOK, "/users") })上述代码中,路由规则
/users会匹配URL路径为/users的GET请求,/users/的GET请求默认会被重定向到/users。 -
参数匹配:
router.GET("/users/:id", func(c *gin.Context) { id := c.Param("id") c.JSON(http.StatusOK, "%s", id) })在上述代码中,路由规则
/users/:id可以匹配URL路径为/users/123的GET请求(匹配不到/user/123/12),其中:id是一个参数,表示用户的ID。通过c.Param方法可以获取到该参数的值,获取的参数为字符串类型。 -
通配符匹配:
router.GET("/users/1/*path", func(c *gin.Context) { path := c.Param("path") c.JSON(http.StatusOK, "%s", path) })在上述代码中,路由规则
/users/*path可以匹配URL路径为/users/abc/def的GET请求,返回的字符串为/abc/def,其中*path表示路径的一部分,可以匹配任意字符。
2.2.4 处理器函数
处理器函数是用于处理请求的函数,它接收一个gin.Context指针类型的参数,gin.Context包含了请求的上下文信息,如请求参数、请求头、响应等。
在Gin框架中,处理器函数负责处理路由匹配到的请求,并生成相应的响应。处理器函数通常包含以下几个步骤:
- 解析请求参数:可以通过
gin.Context的方法来获取请求参数,如Query方法获取URL查询参数,PostForm方法获取表单参数,BindJSON方法解析JSON请求体等。 - 处理业务逻辑:根据请求的目的和操作,编写相应的业务逻辑代码来处理请求。这可能包括查询数据库、调用其他服务、处理数据等。
- 构建响应:根据业务逻辑的结果,构建相应的响应数据。可以使用
gin.Context的方法来设置响应状态码、响应头和响应体。 - 返回响应:通过
gin.Context的方法将响应发送给客户端。
以下是一个简单的处理器函数示例:
func getUser(c *gin.Context) {
userID := c.Param("id") // 获取URL路径参数
// 根据userID查询用户信息的业务逻辑
user := getUserByID(userID)
if user == nil {
c.JSON(http.StatusNotFound, gin.H{"message": "User not found"})
return
}
c.JSON(http.StatusOK, user)
}
在上面的示例中,getUser函数是用于处理获取用户信息的请求。它首先通过c.Param方法获取URL路径中的参数,然后根据该参数查询用户信息。如果找不到用户,返回404状态码和相应的错误消息;如果找到用户,返回200状态码和用户信息。
处理器函数是路由匹配后实际处理请求的地方,通过编写合适的处理器函数,我们可以实现对资源的增删改查等操作,并提供合适的响应给客户端。
2.2.5 参数解析与绑定
当处理 HTTP 请求时,我们通常需要从请求中获取参数,并且可能还需要对参数进行验证。Gin 框架提供了一些方便的方法来解析和绑定参数,并使用 binding 标签进行验证。
以下是一些常用的参数获取方式:
-
获取查询参数:
使用
c.Query()方法可以获取查询参数,例如 URL 中的查询字符串参数。以下是一个示例:func getUser(c *gin.Context) { name := c.Query("name") age := c.Query("age") // 处理逻辑... }在上述示例中,
c.Query()方法用于获取查询字符串参数,如/user?name=John&age=20中的name和age参数。 -
获取路径参数:
使用
c.Param()方法可以获取路径参数,例如 URL 中的占位符参数。以下是一个示例:func getUser(c *gin.Context) { id := c.Param("id") // 处理逻辑... }在上述示例中,
c.Param()方法用于获取 URL 中的路径参数,如/user/:id中的id参数。 -
获取表单参数:
使用
c.PostForm()方法可以获取表单参数,例如 POST 请求中的表单数据。以下是一个示例:func createUser(c *gin.Context) { name := c.PostForm("name") email := c.PostForm("email") // 处理逻辑... }在上述示例中,
c.PostForm()方法用于获取 POST 请求的表单参数。 -
获取请求体参数:
使用
c.ShouldBind()方法可以将请求体中的参数绑定到结构体中。以下是一个示例:type User struct { Name string `form:"name" json:"name" binding:"required"` Email string `form:"email" json:"email" binding:"required,email"` } func createUser(c *gin.Context) { var user User if err := c.ShouldBind(&user); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // 处理逻辑... }在上述示例中,我们定义了一个
User结构体,并使用form和json标签指定了参数的解析方式。通过c.ShouldBind()方法将请求体中的参数绑定到结构体中,并进行参数验证。
通过以上方式,我们可以方便地在 Gin 框架中获取和解析参数,并进行相应的处理和验证。
2.2.6 中间件
中间件是在请求到达处理器函数之前或之后执行的一系列函数。在Gin框架中,中间件可以用于执行一些公共的逻辑,如日志记录、身份验证、错误处理等。
在Gin框架中,可以通过Use方法注册中间件。中间件函数接收一个gin.Context参数,可以在函数中执行一些逻辑,并决定是否继续执行后续的中间件或处理器函数。
以下是一个简单的中间件示例:
func loggerMiddleware(c *gin.Context) {
log.Println("Request received") // 记录日志
c.Next() // 继续执行后续的中间件或处理器函数
log.Println("Request handled") // 记录日志
}
func main() {
router := gin.Default() // 创建一个默认的路由引擎实例
router.Use(loggerMiddleware) // 注册中间件
router.GET("/users", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "Hello, users"})
})
router.Run(":8080") // 启动HTTP服务器
}
在上述示例中,loggerMiddleware函数是一个简单的中间件函数,用于记录请求的日志。在main函数中,通过router.Use方法注册了该中间件。
当有请求到达时,Gin框架会先执行中间件函数,然后再执行后续的处理器函数。在中间件函数中,可以执行一些公共的逻辑,如记录请求日志、验证身份等。
通过使用中间件,我们可以在请求到达处理器函数之前或之后执行一些公共的逻辑,提供更加灵活和可复用的代码。
2.2.7 静态文件服务
静态文件指的是Web应用中的一些固定文件,如HTML、CSS、JavaScript、图片、视频等。这些文件不需要经过处理,直接返回给浏览器即可。在Gin框架中,静态文件通常存储在服务器的某个目录下,例如/public或/static目录。
当使用 Gin 框架处理静态文件时,可以使用以下三种方法中的一种:
-
使用
Static()方法:func main() { router := gin.Default() // 配置静态文件服务 router.Static("/static", "./static") router.Run(":8080") }在上述示例中,我们将
/static路径映射到./static文件夹。这意味着当有请求发送到/static路径时,Gin 框架会自动在./static文件夹下查找并提供对应的静态文件。假设在
./static文件夹下有一个名为1.mp4的视频文件。在浏览器中访问http://localhost:8080/static/1.mp4,它将加载并播放1.mp4视频文件。请确保./static文件夹下存在名为1.mp4的视频文件,并确保你的服务器已经在监听8080端口。这样,当你访问http://localhost:8080/static/1.mp4时,浏览器将加载并播放该视频文件。通过配置静态文件服务,Gin 框架可以帮助你提供静态文件,并自动处理文件的路由。这样,你可以通过浏览器轻松访问和播放静态视频文件。
-
使用
StaticFS()方法:func main() { router := gin.Default() // 将已存在的文件系统映射到 /static 路径上 router.StaticFS("/static", http.Dir("./static")) router.Run(":8080") }在上述示例中,我们使用
StaticFS()方法将一个已存在的文件系统映射到/static路径上。使用http.Dir()函数指定静态文件所在的文件夹路径。 -
使用
StaticFile()方法:func main() { router := gin.Default() // 处理单个静态文件 router.StaticFile("/favicon.ico", "./static/favicon.ico") router.Run(":8080") }在上述示例中,我们使用
StaticFile()方法来处理单个静态文件。我们将/favicon.ico路径映射到./static/favicon.ico文件。
通过以上三种方法之一,Gin 框架可以自动处理静态文件的路由和提供相应的文件内容。你可以根据自己的需求选择适合的方法来处理静态文件。