目录

  • 一、gin快速入门
    • 1 – gin简介
    • 2 – gin快速入门
    • 3 – gin示例原型优化
    • 4 – gin的Default和New
    • 5 – Gin的请求方法
    • 6 – 路由分组
    • 7 – 从url中获取参数
    • 8 – required标记
  • 二、获取表单参数
    • 1 – get获取参数
    • 2- post获取参数
    • 3 – get、post混合获取
  • 三、json与protobuf渲染
    • 1 – json渲染
    • 2 – protobuf渲染
  • 四、表单验证
    • 1 – 登录表单验证
    • 2 – 注册表单验证
    • 3 – 表单验证错误翻译成中文

一、gin快速入门

1 – gin简介

  • gin官方文档地址:https://gin-gonic.com/zh-cn/docs/quickstart/
  • gin的github地址:https://github.com/gin-gonic/gin
  • gin与beego
    • gin是一个轻量级的web框架
    • beego是一个大而全的web框架,集成的东西比较多,国内人员制作的

2 – gin快速入门

  • 下载并安装gin(可选)go get -u github.com/gin-gonic/gin
    • 如果我们使用的是go module的方式,就不需要手动下载,import后自动同步即可
  • gin的importimport "github.com/gin-gonic/gin"
  • gin快速入门
    • 启动后使用浏览器访问:http://127.0.0.1:8080/ping
package mainimport "github.com/gin-gonic/gin"func main() {r := gin.Default()r.GET("/ping", func(c *gin.Context) {c.JSON(200, gin.H{"message": "pong",})})r.Run() // 监听并在 0.0.0.0:8080 上启动服务}

3 – gin示例原型优化

  • r.GET原型
    • param1 -> 相对访问路径
    • param2 -> HandlerFunc类型,可以传入多个
// GET is a shortcut for router.Handle("GET", path, handle).func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {return group.handle(http.MethodGet, relativePath, handlers)}
  • HandlerFunc原型
// HandlerFunc defines the handler used by gin middleware as return value.type HandlerFunc func(*Context)
  • gin.H原型type H map[string]any,因为在gin太经常使用了,所以定义了H
// any is an alias for interface{} and is equivalent to interface{} in all ways.type any = interface{}
  • 根据原型改造示例代码
    • 返回状态码200修改为http.StatusOK
    • 自定义HandlerFunc
    • 修改监听的端口号为8088
    • gin.H体验
package mainimport ("net/http""github.com/gin-gonic/gin")func pong(c *gin.Context) {c.JSON(http.StatusOK, map[string]string{"message": "pong",})}func main() {r := gin.Default()r.GET("/ping", pong)r.Run(":8088") // 监听并在 0.0.0.0:8080 上启动服务}

4 – gin的Default和New

  • gin.Default()源码
    • 可以看到Default会默认帮我添加上Logger和Recovery的中间件,然后再New()
    • 也就是说gin.New()是不会添加Logger和Recovery的中间件的
// Default returns an Engine instance with the Logger and Recovery middleware already attached.func Default() *Engine {debugPrintWARNINGDefault()engine := New()engine.Use(Logger(), Recovery())return engine}

5 – Gin的请求方法

  • 只要为对应的请求配置访问路径与HandlerFunc即可:与之前介绍的Get请求一样的实现
package mainimport "github.com/gin-gonic/gin"func main() {// 使用默认中间件创建一个gin路由器// logger and recovery (crash-free) 中间件router := gin.Default()//restful 的开发中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端口,也可以自己定义启动端口router.Run()// router.Run(":3000") for a hard coded port}

6 – 路由分组

  • 路由分组:当多个请求都有相同前缀的时候就可以使用路由分组来实现
func main() {router := gin.Default()// Simple group: v1v1 := router.Group("/v1"){v1.POST("/login", loginEndpoint)v1.POST("/submit", submitEndpoint)v1.POST("/read", readEndpoint)}// Simple group: v2v2 := router.Group("/v2"){v2.POST("/login", loginEndpoint)v2.POST("/submit", submitEndpoint)v2.POST("/read", readEndpoint)}router.Run(":8082")}

7 – 从url中获取参数

  • 提取变量:以冒号开头的方式,如/:id//:id/:action
package mainimport ("net/http""github.com/gin-gonic/gin")func main() {router := gin.Default()goodsGroup := router.Group("/goods"){//获取商品id为1的详细信息 模式goodsGroup.GET("/:id/", goodsDetail)goodsGroup.GET("/:id/:action", goodsDetailX)goodsGroup.GET("/:id/:action/add", goodsDetailY)}router.Run(":8088")}func goodsDetail(c *gin.Context) {id := c.Param("id")c.JSON(http.StatusOK, gin.H{"id": id,})}func goodsDetailX(c *gin.Context) {id := c.Param("id")action := c.Param("action")c.JSON(http.StatusOK, gin.H{"id": id,"action": action,})}func goodsDetailY(c *gin.Context) {id := c.Param("id")action := c.Param("action")c.JSON(http.StatusOK, gin.H{"id": id,"action": action,})}

8 – required标记

  • 从测试结果分析:添加了required的TAG,如果参数不是uuid返回的是404错误
package mainimport ("net/http""github.com/gin-gonic/gin")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(c *gin.Context) {var person Personif err := c.ShouldBindUri(&person); err != nil {c.Status(404)return}c.JSON(http.StatusOK, gin.H{"name": person.Name,"id": person.ID,})})router.Run(":8088")}

  • 删除掉uuid的限制,并修改ID为int属性ID int uri:"id" binding:"required"
type Person struct {ID int `uri:"id" binding:"required"`Name string `uri:"name" binding:"required"`}


二、获取表单参数

1 – get获取参数

package mainimport ("net/http""github.com/gin-gonic/gin")func main() {router := gin.Default()router.GET("/welcome", welcome)router.Run(":8088")}func welcome(c *gin.Context) {firstName := c.DefaultQuery("firstname", "zhp")lastName := c.DefaultQuery("lastname", "test")// 不加默认值的话就直接使用Query//lastName := c.Query("lastname", "test")c.JSON(http.StatusOK, gin.H{"first_name": firstName,"last_name":lastName,})}

2- post获取参数

package mainimport ("net/http""github.com/gin-gonic/gin")func main() {router := gin.Default()router.POST("/form_post", formPost)router.Run(":8088")}func formPost(c *gin.Context) {message := c.PostForm("message")nick := c.DefaultPostForm("nick", "anonymous")c.JSON(http.StatusOK, gin.H{"message": message,"nick":nick,})}

3 – get、post混合获取

package mainimport ("net/http""github.com/gin-gonic/gin")func main() {router := gin.Default()router.POST("/post", getPost)router.Run(":8088")}func getPost(c *gin.Context) {id := c.Query("id")page := c.DefaultQuery("page", "0")name := c.PostForm("name")message := c.DefaultPostForm("message", "信息")c.JSON(http.StatusOK, gin.H{"id":id,"page":page,"name":name,"message": message,})}



三、json与protobuf渲染

1 – json渲染

  • 增加了json的TAG后:输出中就不是Name了,而是user
package mainimport ("net/http""github.com/gin-gonic/gin")func main() {router := gin.Default()router.GET("/moreJSON", moreJSON)router.Run(":8088")}func moreJSON(c *gin.Context) {var msg struct {Namestring `json:"user"`Message stringNumberint}msg.Name = "bobby"msg.Message = "这是一个测试json"msg.Number = 20c.JSON(http.StatusOK, msg)}

2 – protobuf渲染

  • main.go
package mainimport ("net/http""github.com/gin-gonic/gin""test_project/gin_start/proto")func main() {router := gin.Default()router.GET("/someProtoBuf", returnProto)router.Run(":8088")}func returnProto(c *gin.Context) {course := []string{"python", "go", "微服务"}user := &proto.Teacher{Name: "bobby",Course: course,}c.ProtoBuf(http.StatusOK, user)}
  • user.protoprotoc --go_out=. --go_opt=paths=source_relative *.proto
syntax = "proto3";option go_package = ".;proto";message Teacher {string name = 1;repeated string course = 2;}


四、表单验证

1 – 登录表单验证

  • gin的表单验证使用的是第三方库:github.com/go-playground/validator
  • gin提供了两套方法实现表单验证:gin实际上是基于第三库的基础上进行了封装
    • Must bind:如果不符合要求,就会抛出400的异常
      • Method:Bind(这个仍然调用的是ShouldBind)、BindJSON。。。
    • Should bind:如果不符合要求,会返回给开发人员
      • Method:ShouldBind、ShouldBindJSON。。。
  • 登录表单验证
package mainimport ("fmt""net/http""github.com/gin-gonic/gin")// required:字段必填// min:最小长度// max:最大长度type LoginForm struct {User string `form:"user" json:"user" xml:"user"binding:"required,min=3,max=10"`Password string `form:"password" json:"password" xml:"password"binding:"required"`}func main() {router := gin.Default()router.POST("/loginJSON", func(c *gin.Context) {var loginForm LoginFormif err := c.ShouldBind(&loginForm); err != nil {fmt.Println(err.Error())c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(),})return}c.JSON(http.StatusOK, gin.H{"msg": "登录成功",})})router.Run(":8088")}

2 – 注册表单验证

package mainimport ("fmt""net/http""github.com/gin-gonic/gin")// gte:>=// lte:<=// eqfield:密码一致type SignUpForm struct {Ageuint8`json:"age" binding:"gte=1,lte=130"`Name string `json:"name" binding:"required,min=3"`Emailstring `json:"email" binding:"required,email"`Password string `json:"password" binding:"required"`RePassword string `json:"re_password" binding:"required,eqfield=Password"` //跨字段}func main() {router := gin.Default()router.POST("/signup", func(c *gin.Context) {var signUpFrom SignUpFormif err := c.ShouldBind(&signUpFrom); err != nil {fmt.Println(err.Error())c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(),})return}c.JSON(http.StatusOK, gin.H{"msg": "注册成功",})})router.Run(":8088")}

3 – 表单验证错误翻译成中文

  • 表单验证翻译实现:实现返回的错误是中文,并且是json格式
    • InitTrans:主要完成翻译的初始化,将提示的对象转换成json的小写
    • removeTopStruct:删除掉结构体的前缀,如LoginForm.user -> user
package mainimport ("fmt""net/http""reflect""strings""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")type LoginForm struct {User string `json:"user" binding:"required,min=3,max=10"`Password string `json:"password" binding:"required"`}var trans ut.Translatorfunc InitTrans(locale string) (err error) {//修改gin框架中的validator引擎属性, 实现定制if v, ok := binding.Validator.Engine().(*validator.Validate); ok {//注册一个获取json的tag的自定义方法v.RegisterTagNameFunc(func(fld reflect.StructField) string {name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]if name == "-" { // json TAG如果是"-"不处理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}return}func removeTopStruct(fileds map[string]string) map[string]string {rsp := map[string]string{}for field, err := range fileds {rsp[field[strings.Index(field, ".")+1:]] = err}return rsp}func main() {//代码侵入性很强 中间件if err := InitTrans("zh"); err != nil {fmt.Println("初始化翻译器错误")return}router := gin.Default()router.POST("/loginJSON", func(c *gin.Context) {var loginForm LoginFormif err := c.ShouldBind(&loginForm); err != nil {errs, ok := err.(validator.ValidationErrors)if !ok {c.JSON(http.StatusOK, gin.H{"msg": err.Error(),})}c.JSON(http.StatusBadRequest, gin.H{"error": removeTopStruct(errs.Translate(trans)),})return}c.JSON(http.StatusOK, gin.H{"msg": "登录成功",})})router.Run(":8088")}