gin框架--模板&路由&中间件(小白学习gin框架第二篇)

小白对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,即能得到以下结果。

image-20230914203754868
传递参数给html网页

首先可以编辑你想给html传递的参数

例如,我只想给网页传递一个obj的map格式。

	//如果要在前方获取后文参数
	obj := map[string]interface{}{
		"name":  "wst",
		"age":   18,
		"likes": []string{"吃饭", "奶茶"},
	}

然后只需在下面的t1.Execute中传递obj,即:t1.Execute(w, obj)

image-20230914205701754

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文件中的变量取值,然后运行:

image-20230914205901243
模板的其他相关操作

管道、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测试一下不同的请求方法返回的是什么

image-20230915170738674image-20230915170756134

路由组

目的:后端结构更清晰,也方便整理;但如果不用路由组,就分别使用上面单个路由,效果相同,下面贴示一下如果使用路由组的后端部分代码

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())使其,结果如下所示

image-20230915201712764

ps:我知道这个hook很难,但是是项目中比较实用的,如果想单纯知道中间件的用法可以找一下其他教程了解。后面我会单独写一篇来理解深化一下这个功能吧。

注册中间件

假设其中cost()使一个中间件。

  1. 全局路由

    在所有路由函数之前,使用r.use()

    func main() {
    	r := gin.Default()
    	// 注册一个全局路由的中间件
    	r.Use(cost())
    	r.GET("/hello", func(c *gin.Context) {
    		c.String(200, "hello,world")
    	})
    	r.Run()
    }
    
  2. 单个路由使用

    // 注册一个单个路由的中间件
    r.GET("/hello",cost(), func(c*gin.Context) {
    		c.String(200, "hello,world")
    	})
    	r.Run()
    }
    
  3. 路由组中使用

    //在定义路由组的是否一并定义中间件
    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

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值