小白对Gin的学习(二) — 模板&中间件&路由
模板
基本示例
首先,建立一个 index.html
文件,其中 {{ . }}
直接返回后端要返回的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>go语言的测试界面</title>
</head>
<body>
{{ . }}
</body>
</html>
在与 index.html
文件同层结构下,创建web go文件,即就是创建main程序
package main
import (
"net/http"
"text/template"
)
// 处理进行渲染模板的函数
func tmpl(w http.ResponseWriter, r *http.Request) {
t1, err := template.ParseFiles("index.html")
if err != nil {
panic(err)
}
t1.Execute(w, "好像wst今天啊有点认真欸")
}
func main() {
server := http.Server{
Addr: "127.0.0.1:8080",
}
http.HandleFunc("/index", tmpl)
server.ListenAndServe()
}
运行,在浏览器上方输入 localhost:8080/index
,即能得到以下结果。
传递参数给html网页
首先可以编辑你想给html传递的参数
例如,我只想给网页传递一个obj的map格式。
//如果要在前方获取后文参数
obj := map[string]interface{}{
"name": "wst",
"age": 18,
"likes": []string{"吃饭", "奶茶"},
}
然后只需在下面的t1.Execute中传递obj,即:t1.Execute(w, obj)
go其他程序片段不修改,调整一下html的展示形式,
<!DOCTYPE html>
<html>
<head>
<title>My Template</title>
</head>
<body>
<h1>Hello, {{ .name}}!</h1>
<p>You are {{ .age}} years old.</p>
<p>Your likes:</p>
<ul>
{{range .likes}}
<li>{{.}}</li>
{{ end}}
</ul>
</body>
</html>
用{{.name}}
等来获取go文件中的变量取值,然后运行:
模板的其他相关操作
管道、if,range、with语句、注释、变量赋值等操作再看看吧
自定义模板函数
package main
import (
"fmt"
"html/template"
"net/http"
)
func index(w http.ResponseWriter, r *http.Request) {
// 1、解析指定文件生成模板对象
t1 := template.New("index.html")
t1.Funcs(funcMap).ParseFiles("index.html")
t1.Execute(w, nil)
}
// 自定义模板函数:
func add(x int, y int) int {
return x + y
}
// 将自定义模板其添加到FuncMap结构中,并将此函数命名为"add",以后在待解析的内容中就可以调用"add"函数。
var funcMap = template.FuncMap{
"add": add,
}
func main() {
http.HandleFunc("/", index)
err := http.ListenAndServe(":8099", nil)
if err != nil {
fmt.Println("HTTP server failed,err:", err)
return
}
}
然后要使用只用在html文件中,使用 {{ add 2 3}}
Gin路由
普通路由
r.Get("/user", func(c *gin.Context) {...}
r.Post("/user", func(c *gin.Context) {...}
但是存在 r.Any()
匹配任何路由,下面以例子的形式来说明
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.Any("/user", func(c *gin.Context) {
switch c.Request.Method {
case http.MethodGet:
c.JSON(200, gin.H{
"method": "get",
})
case http.MethodPost:
c.JSON(200, gin.H{
"method": "post",
})
}
})
r.Run()
}
然后我们可以使用postman测试一下不同的请求方法返回的是什么
路由组
目的:后端结构更清晰,也方便整理;但如果不用路由组,就分别使用上面单个路由,效果相同,下面贴示一下如果使用路由组的后端部分代码
userGroup := r.Group("/user")
{
userGroup.GET("/login", func(c *gin.Context) {})
userGroup.POST("/insert", func(c *gin.Context) {})
userGroup.DELETE("/del", func(c *gin.Context) {})
}
courseGroup := r.Group("/user")
{
courseGroup.GET("/login", func(c *gin.Context) {})
courseGroup.POST("/insert", func(c *gin.Context) {})
courseGroup.DELETE("/del", func(c *gin.Context) {})
}
gin路由就是大量公共前缀的树结构
中间件
个人认为中间件是很重要的,其中是在多数情况下,网络业务处理,运行逻辑就是靠其进行,工程师大多喜欢使用中间件来使带啊吗更加有逻辑,结构更清晰。
Gin框架就语序加入用户自己的钩子(Hook)函数
。这个钩子函数就叫中间件
,用来处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。
中间件类型
在Gin中,中间件必须是 gin.HandlerFunc
类型
示例:记录返回的body信息
在中间件的功能函数中,我们先定义了一个responseLoggerWriter
的响应写入器,然后实现write方法,在ResponseLoggerMiddleware()
方法使用。
func ResponseLoggerMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
writer := &responseLoggerWriter{
ResponseWriter: c.Writer,
body: bytes.NewBuffer(nil),
}
c.Writer = writer
c.Next() // 处理请求
fmt.Println("Response body:" + writer.body.String())
}
}
// responseLoggerWriter 是自定义的响应写入器
type responseLoggerWriter struct {
// 这里是ResponseWriter的接口
gin.ResponseWriter
// 缓存存储的body区域
body *bytes.Buffer
}
// Write 是响应写入器的Write方法实现
func (w responseLoggerWriter) Write(data []byte) (int, error) {
//写入相应内容到缓冲区
w.body.Write(data)
// 将响应内容写入到原始的ResponseWriter
return w.ResponseWriter.Write(data)
}
最后在main
中使用r.Use(responseLoggerWriter())
使其,结果如下所示
ps:我知道这个hook很难,但是是项目中比较实用的,如果想单纯知道中间件的用法可以找一下其他教程了解。后面我会单独写一篇来理解深化一下这个功能吧。
注册中间件
假设其中cost()
使一个中间件。
-
全局路由
在所有路由函数之前,使用
r.use()
func main() { r := gin.Default() // 注册一个全局路由的中间件 r.Use(cost()) r.GET("/hello", func(c *gin.Context) { c.String(200, "hello,world") }) r.Run() }
-
单个路由使用
// 注册一个单个路由的中间件 r.GET("/hello",cost(), func(c*gin.Context) { c.String(200, "hello,world") }) r.Run() }
-
路由组中使用
//在定义路由组的是否一并定义中间件 userGroup :=r.Group("/user",cost()) { r.Get("/login",func(c *gin.Context(){...})) r.Post("/view",func(c *gin.Context(){...})) }
或者另一种写法:
userGroup :=r.Group("/user",cost()) //在定义路由组后 定义中间件 userGroup.Use(cost()) { r.Get("/login",func(c *gin.Context(){...})) r.Post("/view",func(c *gin.Context(){...})) }
处理多个服务
1.在main
函数中,直接运用 gin.Default()
得到多个引擎,来进行多个服务;后面用go func()
进行
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
// 创建Gin引擎实例
engine := gin.Default()
// 注册路由和处理函数(服务1)
engine.GET("/service1", func(c *gin.Context) {
c.String(http.StatusOK, "Service 1")
})
// 创建第二个Gin引擎实例
engine2 := gin.Default()
// 注册路由和处理函数(服务2)
engine2.GET("/service2", func(c *gin.Context) {
c.String(http.StatusOK, "Service 2")
})
// 启动多个服务(多个端口)
go func() {
if err := engine.Run(":8080"); err != nil {
fmt.Println("Failed to start service 1:", err)
}
}()
go func() {
if err := engine2.Run(":8081"); err != nil {
fmt.Println("Failed to start service 2:", err)
}
}()
// 阻塞主goroutine
select {}
}
2…在main
函数中,直接运用 engine1 := router1()
得到多个引擎,来进行多个服务;使用http.server
来进行handler
的替换,后面用go func()
进行
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
func main() {
// 创建Gin引擎实例(服务1)
engine1 := router1()
// 创建http.Server实例(服务1)
server1 := &http.Server{
Addr: ":8080",
Handler: engine1,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
// 启动服务1
go func() {
if err := server1.ListenAndServe(); err != nil && err != http.ErrServerClosed {
fmt.Println("Failed to start service 1:", err)
}
}()
// 创建Gin引擎实例(服务2)
engine2 := router2()
// 创建http.Server实例(服务2)
server2 := &http.Server{
Addr: ":8081",
Handler: engine2,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
// 启动服务2
go func() {
if err := server2.ListenAndServe(); err != nil && err != http.ErrServerClosed {
fmt.Println("Failed to start service 2:", err)
}
}()
// 阻塞主goroutine
select {}
}
// router1 是服务1的路由和处理函数
func router1() *gin.Engine {
engine := gin.Default()
engine.GET("/service1", func(c *gin.Context) {
c.String(http.StatusOK, "Service 1")
})
return engine
}
// router2 是服务2的路由和处理函数
func router2() *gin.Engine {
engine := gin.Default()
engine.GET("/service2", func(c *gin.Context) {
c.String(http.StatusOK, "Service 2")
})
return engine
}
}()
// 阻塞主goroutine
select {}
}
// router1 是服务1的路由和处理函数
func router1() *gin.Engine {
engine := gin.Default()
engine.GET("/service1", func(c *gin.Context) {
c.String(http.StatusOK, "Service 1")
})
return engine
}
// router2 是服务2的路由和处理函数
func router2() *gin.Engine {
engine := gin.Default()
engine.GET("/service2", func(c *gin.Context) {
c.String(http.StatusOK, "Service 2")
})
return engine
}