路由
参数匹配
func main() {
router := gin.Default()
// 此 handler 将匹配 /user/john 但不会匹配 /user/ 或者 /user
router.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
c.String(http.StatusOK, "Hello %s", name)
})
// 此 handler 将匹配 /user/john/ 和 /user/john/send
// 如果没有其他路由匹配 /user/john,它将重定向到 /user/john/
router.GET("/user/:name/*action", func(c *gin.Context) {
name := c.Param("name")
action := c.Param("action")
message := name + " is " + action
c.String(http.StatusOK, message)
})
router.Run(":8080")
}
路由组
func main() {
router := gin.Default()
// 简单的路由组: v1
v1 := router.Group("/v1")
{
v1.POST("/login", loginEndpoint)
v1.POST("/submit", submitEndpoint)
v1.POST("/read", readEndpoint)
}
// 简单的路由组: v2
v2 := router.Group("/v2")
{
v2.POST("/login", loginEndpoint)
v2.POST("/submit", submitEndpoint)
v2.POST("/read", readEndpoint)
}
router.Run(":8080")
}
中间件
是位于路由请求处理函数之前或之后执行的处理过程,作用如下。
- 日志记录:记录请求的相关信息,如请求方法、路径、时间等。
- 身份验证和授权:检查用户是否有权访问特定的路由或资源。
- 数据处理和转换:对请求或响应的数据进行预处理或修改。
- 错误处理:捕获和处理在请求处理过程中可能出现的错误。
- 性能监控:测量请求的处理时间等性能指标。
在使用 Gin 框架时,可以使用中间件来为一组路由或整个应用添加通用的处理逻辑,而无需在每个具体的路由处理函数中重复编写相同的代码。设置中间件的方式如下。
func main() {
// 新建一个没有任何默认中间件的路由
r := gin.New()
// 全局中间件
// Logger 中间件将日志写入 gin.DefaultWriter,即使你将 GIN_MODE 设置为 release。
// By default gin.DefaultWriter = os.Stdout
r.Use(gin.Logger())
// Recovery 中间件会 recover 任何 panic。如果有 panic 的话,会写入 500。
r.Use(gin.Recovery())
// 你可以为每个路由添加任意数量的中间件。
r.GET("/benchmark", MyBenchLogger(), benchEndpoint)
// 认证路由组
// authorized := r.Group("/", AuthRequired())
// 和使用以下两行代码的效果完全一样:
authorized := r.Group("/")
// 路由组中间件! 在此例中,我们在 "authorized" 路由组中使用自定义创建的
// AuthRequired() 中间件
authorized.Use(AuthRequired())
{
authorized.POST("/login", loginEndpoint)
authorized.POST("/submit", submitEndpoint)
authorized.POST("/read", readEndpoint)
// 嵌套路由组
testing := authorized.Group("testing")
testing.GET("/analytics", analyticsEndpoint)
}
// 监听并在 0.0.0.0:8080 上启动服务
r.Run(":8080")
}
next()
函数通常用于控制中间件的执行流程。当在一个中间件函数中调用 next()
函数时,它会将控制权传递给下一个中间件或最终的路由处理函数。如果没有调用 next()
,则后续的中间件和路由处理函数将不会被执行。
通过这种方式,可以在中间件中进行一些预处理操作,然后决定是否继续执行后续的处理逻辑。
package main
import (
"github.com/gin-gonic/gin"
)
func myMiddleware(c *gin.Context) {
// 在此处进行一些预处理操作
println("在中间件中进行预处理")
// 调用 next 函数,将控制权传递给后续的中间件或路由处理函数
next(c)
// 在此处进行一些后续处理操作
println("在中间件中进行后续处理")
}
func main() {
r := gin.Default()
// 使用中间件
r.Use(myMiddleware)
r.GET("/hello", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "Hello, World!",
})
})
r.Run(":8080")
}
请求参数绑定
GET/POST请求参数绑定
POST /post?id=1234&page=1 HTTP/1.1
Content-Type: application/x-www-form-urlencoded
name=manu&message=this_is_great
func main() {
router := gin.Default()
router.POST("/post", func(c *gin.Context) {
id := c.Query("id")
page := c.DefaultQuery("page", "0")
name := c.PostForm("name")
message := c.PostForm("message")
fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message)
})
router.Run(":8080")
}
模型绑定和验证
要将请求体绑定到结构体中,使用模型绑定。 Gin目前支持JSON、XML、YAML和标准表单值的绑定(foo=bar&boo=baz)。
使用时,需要在要绑定的所有字段上,设置相应的tag。 例如,使用 JSON 绑定时,设置字段标签为 json:"fieldname"。
Gin提供了两类绑定方法。
Must bind
- Methods - Bind, BindJSON, BindXML, BindQuery, BindYAML,BindBody
- Behavior - 这些方法属于 MustBindWith 的具体调用。 如果发生绑定错误,则请求终止,并触发 c.AbortWithError(400, err).SetType(ErrorTypeBind)。响应状态码被设置为 400 并且 Content-Type 被设置为 text/plain; charset=utf-8。
Should bind
- Methods - ShouldBind, ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAML,ShouldBindBody
- Behavior - 这些方法属于 ShouldBindWith 的具体调用。 如果发生绑定错误,Gin 会返回错误并由开发者处理错误和请求。
使用 Bind 、ShouldBind方法时,Gin 会尝试根据 Content-Type 推断如何绑定。 如果你明确知道要绑定什么,可以使用特定类型的 MustBindWith 或 ShouldBindWith。
你也可以指定必须绑定的字段。 如果一个字段的 tag 加上了 binding:"required"
,但绑定时是空值, Gin 会报错。
// 绑定 JSON
type Login struct {
User string `form:"user" json:"user" xml:"user" binding:"required"`
Password string `form:"password" json:"password" xml:"password" binding:"required"`
}
func main() {
router := gin.Default()
// 绑定 JSON ({"user": "manu", "password": "123"})
router.POST("/loginJSON", func(c *gin.Context) {
var json Login
if err := c.ShouldBindJSON(&json); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if json.User != "manu" || json.Password != "123" {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
})
// 绑定 XML (
// <?xml version="1.0" encoding="UTF-8"?>
// <root>
// <user>manu</user>
// <password>123</password>
// </root>)
router.POST("/loginXML", func(c *gin.Context) {
var xml Login
if err := c.ShouldBindXML(&xml); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if xml.User != "manu" || xml.Password != "123" {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
})
// 绑定 HTML 表单 (user=manu&password=123)
router.POST("/loginForm", func(c *gin.Context) {
var form Login
// 根据 Content-Type Header 推断使用哪个绑定器。
if err := c.ShouldBind(&form); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if form.User != "manu" || form.Password != "123" {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
})
// 监听并在 0.0.0.0:8080 上启动服务
router.Run(":8080")
}
绑定HTML复选框
...
type myForm struct {
Colors []string `form:"colors[]"`
}
...
func formHandler(c *gin.Context) {
var fakeForm myForm
c.ShouldBind(&fakeForm)
c.JSON(200, gin.H{"color": fakeForm.Colors})
}
...
绑定URI
package main
import "github.com/gin-gonic/gin"
type Person struct {
ID string `uri:"id" binding:"required,uuid"`
Name string `uri:"name" binding:"required"`
}
func main() {
route := gin.Default()
route.GET("/:name/:id", func(c *gin.Context) {
var person Person
if err := c.ShouldBindUri(&person); err != nil {
c.JSON(400, gin.H{"msg": err.Error()})
return
}
c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID})
})
route.Run(":8088")
}
响应渲染静态资源绑定
JSON响应
以JSON格式组织响应数据,如gin.Context.JSON()
、gin.Context.AsciiJSON()
、gin.Context.PureJSON()
、gin.Context.SecureJSON()
gin.Context.JSON()
以标准的 JSON 格式响应数据。
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/json", func(c *gin.Context) {
data := gin.H{
"message": "Hello, JSON!",
"number": 42,
}
c.JSON(200, data)
})
r.Run(":8080")
}
gin.Context.AsciiJSON()
,将数据编码为 ASCII 格式的 JSON 字符串。它会将非 ASCII 字符进行转义。
gin.Context.PureJSON()
。通常,JSON 使用 unicode 替换特殊 HTML 字符,例如 < 变为 \ u003c。如果要按字面对这些字符进行编码,则可以使用 PureJSON。Go 1.6 及更低版本无法使用此功能。
gin.Context.SecureJSON()
,主要用于防止 JSON 劫持等安全问题。在某些情况下,浏览器可能会误解 JSON 响应并将其错误地解释为脚本执行,SecureJSON
通常会添加一些预防措施来避免这种情况。
XML/JSON/YAML/ProtoBuf 响应渲染
func main() {
r := gin.Default()
// gin.H 是 map[string]interface{} 的一种快捷方式
r.GET("/someJSON", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
})
r.GET("/moreJSON", func(c *gin.Context) {
// 你也可以使用一个结构体
var msg struct {
Name string `json:"user"`
Message string
Number int
}
msg.Name = "Lena"
msg.Message = "hey"
msg.Number = 123
// 注意 msg.Name 在 JSON 中变成了 "user"
// 将输出:{"user": "Lena", "Message": "hey", "Number": 123}
c.JSON(http.StatusOK, msg)
})
r.GET("/someXML", func(c *gin.Context) {
c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
})
r.GET("/someYAML", func(c *gin.Context) {
c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
})
r.GET("/someProtoBuf", func(c *gin.Context) {
reps := []int64{int64(1), int64(2)}
label := "test"
// protobuf 的具体定义写在 testdata/protoexample 文件中。
data := &protoexample.Test{
Label: &label,
Reps: reps,
}
// 请注意,数据在响应中变为二进制数据
// 将输出被 protoexample.Test protobuf 序列化了的数据
c.ProtoBuf(http.StatusOK, data)
})
// 监听并在 0.0.0.0:8080 上启动服务
r.Run(":8080")
}
html模板渲染响应
HTML 模板是包含固定的 HTML 结构以及可替换或动态部分的文件。这些可替换的部分通常使用特定的语法或标记来表示,以便在运行时插入动态数据。
Gin 框架中使用 HTML 模板的一般步骤如下。
- 创建模板文件:创建
.html
文件,例如index.html
,在其中定义 HTML 结构,并使用特定的语法(如{{.VariableName}}
)来标记需要动态填充的部分。 - 加载模板:在 Gin 框架的初始化代码中,使用
LoadHTMLGlob
或LoadHTMLFiles
方法加载模板文件。
r := gin.Default()
r.LoadHTMLGlob("templates/*") // 加载 templates 目录下的所有模板文件
// 或者
r.LoadHTMLFiles("templates/index.html") // 加载指定的模板文件
- 加载与使用html模板
r.GET("/", func(c *gin.Context) {
data := gin.H{
"Title": "My Page Title",
"Content": "This is the content of the page.",
}
c.HTML(http.StatusOK, "index.html", data) // 渲染模板并传递数据
})
设置静态资源
func main() {
router := gin.Default()
router.Static("/assets", "./assets")
router.StaticFS("/more_static", http.Dir("my_file_system"))
router.StaticFile("/favicon.ico", "./resources/favicon.ico")
// 监听并在 0.0.0.0:8080 上启动服务
router.Run(":8080")
}
设置日志
定义日志输出文件
func main() {
// 禁用控制台颜色,将日志写入文件时不需要控制台颜色。
gin.DisableConsoleColor()
// 记录到文件。
f, _ := os.Create("gin.log")
gin.DefaultWriter = io.MultiWriter(f)
// 如果需要同时将日志写入文件和控制台,请使用以下代码。
// gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
router := gin.Default()
router.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
router.Run(":8080")
}
自定义日志格式,处理请求时会自动记录信息如请求方法、路径、状态码、请求花费时间
func main() {
router := gin.New()
// LoggerWithFormatter 中间件会写入日志到 gin.DefaultWriter
// 默认 gin.DefaultWriter = os.Stdout
router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
// 你的自定义格式
return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n",
param.ClientIP,
param.TimeStamp.Format(time.RFC1123),
param.Method,
param.Path,
param.Request.Proto,
param.StatusCode,
param.Latency,
param.Request.UserAgent(),
param.ErrorMessage,
)
}))
router.Use(gin.Recovery())
router.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
router.Run(":8080")
}
自定义路由注册时日志打印的格式
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {
log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers)
}
r.POST("/foo", func(c *gin.Context) {
c.JSON(http.StatusOK, "foo")
})
r.GET("/bar", func(c *gin.Context) {
c.JSON(http.StatusOK, "bar")
})
r.GET("/status", func(c *gin.Context) {
c.JSON(http.StatusOK, "ok")
})
// 监听并在 0.0.0.0:8080 上启动服务
r.Run()
}
其他功能
上传文件、设置和获取cookie、goroutine的使用
http重定向、路由重定向
支持http2.0主动推送功能、使用JSONP实现跨域请求响应
标签:http,func,router,golang,JSON,Context,gin,Gin From: https://www.cnblogs.com/essays/p/18678488