Gin 初步使用

news/2024/11/17 9:51:28/

快速入门

官方文档

引入

import "github.com/gin-gonic/gin"

编写代码

package mainimport ("github.com/gin-gonic/gin""net/http"
)func pong(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "pong",})
}func main() {//实例化一个gin的对象r := gin.Default()r.GET("/ping", pong)r.Run() // 监听并在 0.0.0.0:8080 上启动服务
}

访问

http://127.0.0.1:8080/ping

请求处理

//实例化一个gin的对象
//r := gin.Default() //默认会开启两个中间件,logger和recovery错误恢复
func main() {// 禁用控制台颜色// gin.DisableConsoleColor()// 使用默认中间件(logger 和 recovery 中间件)创建 gin 路由router := gin.Default()router.GET("/someGet", getting)router.POST("/somePost", posting)router.PUT("/somePut", putting)router.DELETE("/someDelete", deleting)router.PATCH("/somePatch", patching)router.HEAD("/someHead", head)router.OPTIONS("/someOptions", options)// 默认在 8080 端口启动服务,除非定义了一个 PORT 的环境变量。router.Run()// router.Run(":3000") hardcode 端口号
}

RESTFUL

路由分组


package mainimport "github.com/gin-gonic/gin"func main() {router := gin.Default()// 简单的路由组: v1v1 := router.Group("/v1"){v1.POST("/login", loginEndpoint)v1.POST("/submit", submitEndpoint)v1.POST("/read", readEndpoint)}// 简单的路由组: v2v2 := router.Group("/v2"){v2.POST("/login", loginEndpoint)v2.POST("/submit", submitEndpoint)v2.POST("/read", readEndpoint)}router.Run(":8080")
}

例如商品组

package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {router := gin.Default()goodsGroup := router.Group("/goods"){goodsGroup.GET("/list", goodsList)goodsGroup.GET("/1", goodsDetail)goodsGroup.POST("/add", goodsAdd)} //方便查看,逻辑组织在一起router.Run(":8080")
}func goodsAdd(context *gin.Context) {context.JSON(http.StatusOK, gin.H{"message": "add successful",})
}func goodsDetail(context *gin.Context) {context.JSON(http.StatusOK, gin.H{"1": "details",})
}func goodsList(context *gin.Context) {context.JSON(http.StatusOK, gin.H{"Goods": []int{1, 2, 3},})
}

url参数解析

关键

router.GET("/user/:name", func(c *gin.Context) {}
package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {router := gin.Default()goodsGroup := router.Group("/goods"){goodsGroup.GET("", goodsList)goodsGroup.GET("/:id", goodsDetail)goodsGroup.POST("", goodsAdd)} //方便查看,逻辑组织在一起router.Run(":8080")
}func goodsAdd(context *gin.Context) {context.JSON(http.StatusOK, gin.H{"message": "add successful",})
}func goodsDetail(context *gin.Context) {param := context.Param("id")context.JSON(http.StatusOK, gin.H{param: "details",})
}func goodsList(context *gin.Context) {context.JSON(http.StatusOK, gin.H{"Goods": []int{1, 2, 3},})
}

约束参数类型

package mainimport ("github.com/gin-gonic/gin""net/http"
)type Person struct {ID   string    `uri:"id" binding:"required,uuid"`Name string `uri:"name" binding:"required"`
}func main() {router := gin.Default()router.GET("/:name/:id", func(context *gin.Context) {var person Personif err := context.ShouldBindUri(&person); err != nil {context.Status(404)return}context.JSON(http.StatusOK, gin.H{"name": person.Name,"id":   person.ID,})})router.Run(":8080")
}

如果使用

http://127.0.0.1:8080/tom/1

则报错404

如果使用

http://127.0.0.1:8080/tom/d4054e14-7479-a070-bf9f-04cfa7bf75c9

则正常

如果整数id,约束

type Person struct {ID   int    `uri:"id" binding:"required`Name string `uri:"name" binding:"required"`
}

Get参数

package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {router := gin.Default()router.GET("/welcome", welcome)router.Run(":8080")
}func welcome(context *gin.Context) {firstName := context.DefaultQuery("firstname", "Tom")lastName := context.DefaultQuery("lastname", "Steve")context.JSON(http.StatusOK, gin.H{"firstName": firstName,"lastName":  lastName,})
}

访问

http://127.0.0.1:8080/welcome?firstname=Jack&lastname=H

有默认值

POST参数

package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {router := gin.Default()router.GET("/welcome", welcome)router.POST("/form_post", formPost)router.Run(":8080")
}func formPost(context *gin.Context) {message := context.PostForm("message")nick := context.DefaultPostForm("nick", "anonymous")context.JSON(http.StatusOK, gin.H{"message": message,"nick":    nick,})
}func welcome(context *gin.Context) {firstName := context.DefaultQuery("firstname", "Tom")lastName := context.DefaultQuery("lastname", "Steve")context.JSON(http.StatusOK, gin.H{"firstName": firstName,"lastName":  lastName,})
}

混合,即url带参数

func getPost(context *gin.Context) {id := context.Query("id")name := context.PostForm("name")context.JSON(http.StatusOK, gin.H{"id":   id,"name": name,})
}

返回数据格式

package mainimport ("github.com/gin-gonic/gin""github.com/gin-gonic/gin/testdata/protoexample""net/http"
)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 stringNumber  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")
}

反解Protobuf使用,proto.Unmarshal

表单验证

gin使用了validator库,https://github.com/go-playground/validator

使用时,需要在要绑定的所有字段上,设置相应的tag。

Gin提供了两类绑定方法:

  • Must bind
    • Methods - Bind, BindJSON, BindXML, BindQuery, BindYAML
    • Behavior - 这些方法属于 MustBindWith 的具体调用。 响应状态码被设置为 400 。
  • Should bind
    • Methods - ShouldBind, ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAML
    • Behavior - 这些方法属于 ShouldBindWith 的具体调用。 如果发生绑定错误,Gin 会返回错误并由开发者处理错误和请求。

使用 Bind 方法时,Gin 会尝试根据 Content-Type 推断如何绑定。 如果你明确知道要绑定什么,可以使用 MustBindWithShouldBindWith

你也可以指定必须绑定的字段。 如果一个字段的 tag 加上了 binding:"required",但绑定时是空值, Gin 会报错。

需要支持什么格式,则指定什么约束

package mainimport ("fmt""github.com/gin-gonic/gin""net/http"
)// 绑定 JSON
type Login struct {User     string `json:"user" binding:"required"`Password string `json:"password" binding:"required"`
}type SignUpForm struct {Age        uint8  `json:"age" binding:"gte=1,lte=130"`Name       string `json:"name" binding:"required,min=3"`Email      string `json:"email" binding:"required,email"`Password   string `json:"password" binding:"required"`RePassword string `json:"rePassword" binding:"required,eqfield=Password"`
}func main() {router := gin.Default()router.POST("/loginJSON", func(context *gin.Context) {var loginForm Loginif err := context.ShouldBind(&loginForm); err != nil {fmt.Println(err.Error())context.JSON(http.StatusBadRequest, gin.H{"error": err.Error(),})return}context.JSON(http.StatusOK, gin.H{"user":     loginForm.User,"password": loginForm.Password,})})router.POST("/signup", func(context *gin.Context) {var signForm SignUpFormif err := context.ShouldBind(&signForm); err != nil {fmt.Println(err.Error())context.JSON(http.StatusBadRequest, gin.H{"error": err.Error(),})return}context.JSON(http.StatusOK, gin.H{"message": "注册成功",})})router.Run(":8080")
}
POST http://127.0.0.1:8080/signup
{"age":130,"name":"Tom","email":"123456789@163.com","password":"1234","rePassword":"1234"
}

中文提示信息;字段问题

package mainimport ("fmt""github.com/gin-gonic/gin""github.com/gin-gonic/gin/binding""github.com/go-playground/locales/en""github.com/go-playground/locales/zh"ut "github.com/go-playground/universal-translator""github.com/go-playground/validator/v10"en_translations "github.com/go-playground/validator/v10/translations/en"zh_translations "github.com/go-playground/validator/v10/translations/zh""net/http""reflect""strings"
)// 绑定 JSON
type Login struct {User     string `json:"user" binding:"required,max=10"`Password string `json:"password" binding:"required"`
}type SignUpForm struct {Age        uint8  `json:"age" binding:"gte=1,lte=130"`Name       string `json:"name" binding:"required,min=3"`Email      string `json:"email" binding:"required,email"`Password   string `json:"password" binding:"required"`RePassword string `json:"rePassword" binding:"required,eqfield=Password"`
}var trans ut.Translatorfunc removeTopStruct(fileds map[string]string) map[string]string {res := map[string]string{}for filed, err := range fileds {res[filed[strings.IndexAny(filed, ".")+1:]] = err}return res
}func InitTrans(locale string) (err error) {//修改gin中的validatorif v, ok := binding.Validator.Engine().(*validator.Validate); ok {//注册一个获取json等待tag的自定义方法;用tag标签的名称v.RegisterTagNameFunc(func(field reflect.StructField) string {name := strings.SplitN(field.Tag.Get("json"), ",", 2)[0]if name == "-" {return ""}return name})zhT := zh.New()enT := en.New()uni := ut.New(enT, zhT, enT) //第一个是首选,第二个是备用trans, ok = uni.GetTranslator(locale)if !ok {return fmt.Errorf("uni.GetTranslator(%s)", locale)}switch locale {case "en":en_translations.RegisterDefaultTranslations(v, trans)case "zh":zh_translations.RegisterDefaultTranslations(v, trans)default:en_translations.RegisterDefaultTranslations(v, trans)}}return
}func main() {if err := InitTrans("zh"); err != nil {fmt.Println("初始化翻译器错误")return}router := gin.Default()router.POST("/loginJSON", func(context *gin.Context) {var loginForm Loginif err := context.ShouldBind(&loginForm); err != nil {errs, ok := err.(validator.ValidationErrors)if !ok {fmt.Println("not ok")context.JSON(http.StatusOK, gin.H{"error": err.Error(),})return}context.JSON(http.StatusOK, gin.H{"error": removeTopStruct(errs.Translate(trans)),})return}context.JSON(http.StatusOK, gin.H{"user":     loginForm.User,"password": loginForm.Password,})})router.POST("/signup", func(context *gin.Context) {var signForm SignUpFormif err := context.ShouldBind(&signForm); err != nil {fmt.Println(err.Error())context.JSON(http.StatusBadRequest, gin.H{"error": err.Error(),})return}context.JSON(http.StatusOK, gin.H{"message": "注册成功",})})router.Run(":8080")
}

自定义中间件

中间件使用

router := gin.New()//全局
//使用logger中间件
router.Use(gin.Logger())
//使用recovery
router.Use(gin.Recovery())//局部使用
authorized := router.Group("/goods")
authorized.Use(AuthRequred)

有一个执行队列,return只是当前中间件结束,

所有要使用context.Abort()

package mainimport ("fmt""github.com/gin-gonic/gin""net/http""time"
)func MyLogger() gin.HandlerFunc {return func(context *gin.Context) {t := time.Now()context.Set("example", "123456")context.Next()end := time.Since(t)fmt.Println("时间消耗: ", end)status := context.Writer.Status()fmt.Println("状态 ", status)}
}
func TokenRequired() gin.HandlerFunc {return func(context *gin.Context) {var token stringfor k, v := range context.Request.Header {if k == "X-Token" {token = v[0]if token != "Tom" {context.JSON(http.StatusUnauthorized, gin.H{"msg": "未登录",})//return不能阻止context.Abort()}}}context.Next()}}
func main() {router := gin.Default()router.Use(TokenRequired())router.GET("/ping", func(context *gin.Context) {context.JSON(http.StatusOK, gin.H{"message": "pong",})})router.Run(":8080")
}

设置静态文件和HTML文件

package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {router := gin.Default()//相对目录router.LoadHTMLFiles("templates/index.tmpl")router.GET("/", func(context *gin.Context) {context.HTML(http.StatusOK, "index.tmpl", gin.H{"Title": "Hello",})})router.Run(":8080")
}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>{{.Title}}</title>
</head>
<body><h1></h1>
</body>
</html>

在命令行访问。

二级目录,与同名模板

package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {router := gin.Default()//相对目录//router.LoadHTMLFiles("templates/index.tmpl", "templates/goods.html")router.LoadHTMLGlob("templates/**/*")router.GET("/", func(context *gin.Context) {context.HTML(http.StatusOK, "index.tmpl", gin.H{"Title": "Hello",})})router.GET("/goods", func(context *gin.Context) {context.HTML(http.StatusOK, "goods.html", gin.H{"name": "一些商品",})})router.GET("/goods/list", func(context *gin.Context) {context.HTML(http.StatusOK, "goods/list.html", gin.H{})})router.GET("/user/list", func(context *gin.Context) {context.HTML(http.StatusOK, "user/list.html", gin.H{})})router.Run(":8080")
}
{{define "goods/list.html"}}{{end}}

静态文件

对应

router.Static("/static", "./static")<link rel="stylesheet" href="/static/style.css">

退出程序

用协程,管道

package mainimport ("context""log""net/http""os""os/signal""time""github.com/gin-gonic/gin"
)func main() {router := gin.Default()router.GET("/", func(c *gin.Context) {time.Sleep(5 * time.Second)c.String(http.StatusOK, "Welcome Gin Server")})srv := &http.Server{Addr:    ":8080",Handler: router,}go func() {// 服务连接if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {log.Fatalf("listen: %s\n", err)}}()// 等待中断信号以优雅地关闭服务器(设置 5 秒的超时时间)quit := make(chan os.Signal)signal.Notify(quit, os.Interrupt)<-quitlog.Println("Shutdown Server ...")ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)defer cancel()if err := srv.Shutdown(ctx); err != nil {log.Fatal("Server Shutdown:", err)}log.Println("Server exiting")
}

http://www.ppmy.cn/news/533401.html

相关文章

Jmeter简易脚本录制回放实例解析

此时jmeter脚本已经录制下来了&#xff0c;但是这个脚本是无法回放的&#xff01;因为“HTTP代理服务器”是“非测试元件”&#xff0c;所以“运行测试计划”的时候&#xff0c;自然就不会运行“非测试元件”了。那么要如何让录制下来的脚本变得可以运行呢&#xff1f;下面就来…

【Python】实现键盘鼠标动作录制和执行的小工具

突发奇想做一个可以实现鼠标键盘操作录制&#xff0c;并可以回放操作的小工具。依托于pynput模块来实现鼠标键盘的控制&#xff0c;tkinter来实现图形界面的绘制。分为以下几个步骤&#xff1a; 一 录制&#xff08;记录过程&#xff0c;并将用户的操作保存为json文件&#xf…

如何克服录制回放模式的弊端

Web浏览器的可视化界面及交互操作屏蔽了与服务器端交互的请求响应的复杂性。例如&#xff0c;我们随便打开一个网页或者触发一次交互操作&#xff0c;就可能在后端触发了成百上千的HTTP请求响应的处理&#xff0c;这些过程对使用者来说是不可见的&#xff0c;也是不需要关心的。…

web页面录制与回放全栈小项目

web页面录制与回放全栈小项目 技术栈相关文档项目代码项目效果本地启动前端后端node 推荐阅读 你越是认真生活&#xff0c;你的生活就会越美好——弗兰克劳埃德莱特 《人生果实》经典语录 技术栈 前端 Vue 后端 Node Egg 数据库 mongo 录制与回访实现 rrweb websocket …

rosbag录制和回放

rosbag录制和回放 1. 录制2. 回放3. 参考 本博客参照一位大佬YongqiangGao的博客实现仿真环境中一个 7 轴机械臂画圆时的 joint_states 数据&#xff0c;在此记录以供自己加深印象以及回顾&#xff0c;主要总结了如何通过rosbag将ROS系统运行过程中的数据录制到 .bag 文件中&am…

15-bag的录制,回放与解析

本教程教你如何使用rosbag record工具来录制ros通信数据。之后在通过rosbag play形式回放录制的数据包 此教程不详细解读&#xff0c;仅将rosbag record对应的help文档列出&#xff0c;并给出具体录制例子 rosbag bag包是存储ROS消息数据的文件格式&#xff0c;rosbag命令可以…

C++用钩子模仿按键精灵录制键盘鼠标的功能

最近需要做个跟按键精灵类似的软件&#xff0c;模仿键盘鼠标的动作&#xff0c;然后重播一次&#xff0c;最开时候的时候用键盘钩子和鼠标钩子做的&#xff0c;虽然实现了功能&#xff0c;但是鼠标移动速度非常快&#xff0c;所以换成WH_JOURNALPLAYBACK 和WH_JOURNALRECORD 钩…

LR11录制回放HTTPS时的问题

背景&#xff1a;使用LoadRunner11录制HTTPS网页&#xff0c;测试性能 环境&#xff1a;LoadRunner11 Patch4 Win7 Firefox45 问题&#xff1a;录制时&#xff0c;出现启动了浏览器但地址栏无URL&#xff0c;页面空白&#xff1b;或者能打开页面但无event的情况。 目录 目…