目录
前言
- 假设一个场景,我们需要统计每个函数的调用时长;我们如果在每个函数开头记录时间,结束时计算消耗时间,这是非常不通用的,并且代码的侵入性很强
- 这时候就可以使用中间件的解决方案来解决上述问题
一、中间件的使用
- router := gin.Default():默认会使用gin的Logger和Recovery的中间件
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
router := gin.New()
// 使用logger、recovery中间件,全局所有
router.Use(gin.Logger(), gin.Recovery())
// 中间件AuthRequired只有调用"/goods"开头的URL才有效
authrized := router.Group("/goods")
authrized.Use(AuthRequired)
router.Run(":8088")
}
func AuthRequired(context *gin.Context) {
}
二、自定义中间件
- 需求实现:使用自定义的中间件,统计每个请求的耗时
package main
import (
"fmt"
"net/http"
"time"
"github.com/gin-gonic/gin"
)
func MyLogger() gin.HandlerFunc {
return func(c *gin.Context) {
t := time.Now()
c.Set("example", "123456")
//让原本改执行的逻辑继续执行
c.Next()
end := time.Since(t)
fmt.Printf("耗时:%v\n", end)
status := c.Writer.Status()
fmt.Println("状态", status)
}
}
func main() {
router := gin.Default()
router.Use(MyLogger())
router.GET("/ping", func(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
router.Run(":8088")
}
三、终止中间件后续逻辑的执行
- 在中间件中要使用Abort才能阻止后续逻辑的执行:不能使用return来执行
package main
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
func TokenRequired() gin.HandlerFunc {
return func(c *gin.Context) {
var token string
for k, v := range c.Request.Header {
if k == "x-Token" { // 这里一定要使用大写
token = v[0]
}
fmt.Println(k, v)
}
if token != "test" {
c.JSON(http.StatusUnauthorized, gin.H{
"msg": "未登陆",
})
// return
c.Abort() // 如果要跳过原有的执行逻辑,不能使用return
}
c.Next()
}
}
func main() {
router := gin.Default()
router.Use(TokenRequired())
router.GET("/ping", func(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
router.Run(":8088")
}
四、gin返回html
1 - 使用gin返回html
package main
import (
"fmt"
"net/http"
"os"
"path/filepath"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
fmt.Println("path -> ", dir)
// LoadHTMLFiles会将指定的目录下的文件加载好, 相对目录
router.LoadHTMLFiles("templates/index.tmpl")
router.GET("/index", func(ctx *gin.Context) {
ctx.HTML(http.StatusOK, "index.tmpl", gin.H{
"title": "学习",
})
})
router.Run(":8088")
}
- index.tmpl
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>
{{ .title }}
</h1>
</body>
</html>
2 - 加载多个html文件
package main
import (
"fmt"
"net/http"
"os"
"path/filepath"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
fmt.Println("path -> ", dir)
// LoadHTMLFiles会将指定的目录下的文件加载好, 相对目录
//router.LoadHTMLFiles("templates/index.tmpl", "templates/goods.html")
// 上面的写法如果文件太多,还需要1个个写太复杂,这个直接加载对应目录的所有文件
router.LoadHTMLGlob("templates/*")
router.GET("/index", func(ctx *gin.Context) {
ctx.HTML(http.StatusOK, "index.tmpl", gin.H{
"title": "学习",
})
})
router.GET("/goods", func(c *gin.Context) {
c.HTML(http.StatusOK, "goods.html", gin.H{
"name": "微服务开发",
})
})
router.Run(":8088")
}
- goods.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>
{{ .name }}
</h1>
</body>
</html>
3 - 同名文件冲突问题
- 问题引入:如果在不同文件夹下有相同名称的文件时,如何解决冲突
- 在html开头加入:{{define “users/list.html”}}
- 在html结尾加入:{{end}}
如果没有在模板中使用define定义 那么我们就可以使用默认的文件名来找
- main.go
package main
import (
"fmt"
"net/http"
"os"
"path/filepath"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
fmt.Println("path -> ", dir)
// LoadHTMLFiles会将指定的目录下的文件加载好, 相对目录
router.LoadHTMLGlob("templates/**/*")
router.GET("/goods/list", func(c *gin.Context) {
c.HTML(http.StatusOK, "goods/list.html", gin.H{
"title": "商品",
})
})
router.GET("/users/list", func(c *gin.Context) {
c.HTML(http.StatusOK, "users/list.html", gin.H{
"title": "用户",
})
})
router.Run(":8088")
}
- template/goods/list.html
{{define "goods/list.html"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>
商品列表页
</h1>
</body>
</html>
{{end}}
- template/users/list.html
{{define "users/list.html"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>
用户列表页
</h1>
</body>
</html>
{{end}}
4 - static静态文件处理
- static静态文件加载:
router.Static("/static", "./static")
五、优雅的退出gin
- 什么是优雅退出:当我们关闭程序的时候应该做的后续处理
- 分析:微服务 -> 启动前或启动之后会做一件事,将当前的服务ip地址和端口号注册到注册中心 -> 当前的服务停止了之后,并没有告知注册中心
- gin官方文档优雅退出:https://gin-gonic.com/docs/examples/graceful-restart-or-stop/
package main
import (
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.GET("/", func(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{
"msg": "pong",
})
})
go func() {
router.Run(":8088")
}()
// 不要使用kill -9 命令,程序无法捕获该信号
quit := make(chan os.Signal)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
// 处理后续的逻辑
fmt.Println("关闭server中。。。")
fmt.Println("注销服务。。。")
}
- 在cmd中运行,并执行CTRL+C