官方地址:gin-gonic.com/docs/

安装与简单测验

下载并安装Gin包,并导入引用

$ go get -u github.com/gin-gonic/gin
//将gin引进到代码中
import "github.com/gin-gonic/gin"
//可选。假如运用比方 http.StatusOK 之类的常量,则需求引进 net/http 包
import "net/http"

编写如下测验代码

package main
import "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 上发动服务
}

然后在浏览器中进行测验: http://localhost:8080 就能够拜访到呼应的恳求。

常见恳求与分组恳求

下面展现几种其他 常规 恳求办法

// 下面列出常用几种恳求办法
r.GET("/someGet", handle)
r.POST("/somePost", handle)
r.PUT("/somePut", handle)
r.DELETE("/someDelete", handle)
r.PATCH("/somePatch", handle)
r.HEAD("/someHead", handle)
r.OPTIONS("/someOptions", handle)
r.Any("/any", handle)

**还能够对恳求进行分组操作。 **

v1 := r.Group("/v1")
{
   v1.GET("/login", handle)
}
v2 := r.Group("/v2")
{
   v2.GET("/login", handle)
}

下面比方 是** 取得恳求中path**

func main() {
   router := gin.Default()
   // 匹配/user/john
   router.GET("/user/:name", func(c *gin.Context) {
      name := c.Param("name")
      c.String(http.StatusOK, "Hello %s", name)
   })
   // 匹配/user/john/和/user/john/send
   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")
}

/user/:name/*action : 表明对后边路由全部含糊匹配。例如:/user/john/send/1 方式 action会匹配得到 name 是 john, action是 /send/1

获取参数 与 参数合法性验证

取得query中参数

func main() {
   router := gin.Default()
   // welcome?firstname=Jane&lastname=Doe
   router.GET("/user", func(c *gin.Context) {
      firstname := c.DefaultQuery("name", "kim") // 获取query中的name,没有的话就为kim
      lastname := c.Query("age")                 // 获取query中的age
      c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
   })
   router.Run(":8080")
}

取得multipart/urlencoded form中的参数

func main() {
   router := gin.Default()
   router.POST("/form_post", func(c *gin.Context) {
      message := c.PostForm("age")
      nick := c.DefaultPostForm("name", "kim")
      c.JSON(200, gin.H{
         "status":  "posted",
         "message": message,
         "nick":    nick,
      })
   })
   router.Run(":8080")
}

curl http://127.0.0.1:8080/form\_post -X POST -d ‘name=john&age=25’

经过此curl发送post恳求,即可取得表单中数据。

模型绑定和参数验证

基本用法

咱们已经见识了x-www-form-urlencoded类型的参数处理,现在越来越多的运用习惯运用JSON来通信,也便是不管回来的response仍是提交的request,其content-type类型都是application/json的格局。而关于一些旧的web表单页仍是x-www-form-urlencoded的方式,这就需求咱们的服务器能支撑住这多种content-type的参数了。

由于go是静态言语,需求先完成界说数据模型,这就需求用到gin的model bind功用了。

gin运用go-playground/validator.v8验证参数,查看完整文档。

需求在绑定的字段上设置tag,比方,绑定格局为json,需求这样设置json:”fieldname” 。

此外,Gin还供给了两套绑定办法:

  1. Must bind
  • Methods – Bind, BindJSON, BindXML, BindQuery, BindYAML

  • Behavior – 这些办法底层运用 MustBindWith,假如存在绑定过错,恳求将被以下指令间断 .

c.AbortWithError(400, err).SetType(ErrorTypeBind),

呼应状况代码会被设置为400,恳求头Content-Type被设置为text/plain; charset=utf-8。

留意,假如你试图在此之后设置呼应代码,将会宣布一个正告 [GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422,假如你期望更好地操控行为,请运用ShouldBind相关的办法

  1. Should bind
  • Methods – ShouldBind, ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAML

  • Behavior – 这些办法底层运用 ShouldBindWith,假如存在绑定过错,则回来过错,开发人员能够正确处理恳求和过错。

当咱们运用绑定办法时,Gin会依据Content-Type推断出运用哪种绑定器,假如你确认你绑定的是什么,你能够运用MustBindWith或许BindingWith。

你还能够给字段指定特定规矩的润饰符,假如一个字段用binding:”required”润饰,而且在绑定时该字段的值为空,那么将回来一个过错。

type Person struct {
   Name string `json:"name" binding:"required"`      // json格局从name取值,而且该值为有必要的
   Age  int    `json:"age" binding:"required,gt=20"` // json格局从age取值,而且该值为有必要的,且有必要大于20
}
func main() {
   router := gin.Default()
   router.POST("/test", func(context *gin.Context) {
      var person Person
      // 这儿我确认传过来的一定是JSON所以用ShouldBindJSON,不然能够用ShouldBind
      if err := context.ShouldBindJSON(&person); err != nil {
         context.JSON(http.StatusBadRequest, gin.H{
            "error": err.Error(),
         })
         return
      }
      context.JSON(http.StatusOK, gin.H{
         "success": true,
      })
   })
   router.Run(":8080")
}
curl http://localhost:8080/test -X POST -d '{"name":"Rock","age": 25}'

上面是经过json映射办法 绑定 恳求数据,而且对恳求数据进行验证。

自界说参数验证

验证包 gopkg.in/go-playground/validator.v8运用办法

package main
import (
   "net/http"
   "reflect"
   "time"
   "github.com/gin-gonic/gin"
   "github.com/gin-gonic/gin/binding"
   "gopkg.in/go-playground/validator.v8"
)
type Booking struct {
   // 这儿的验证办法为bookabledate
   CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
   // gtfield=CheckIn表明大于的字段为CheckIn
   CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
}
func bookableDate(
   v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value,
   field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string,
) bool {
   // 这儿有两个知识点,映射和断语
   // 在这儿,field是一个reflect.Type的接口类型变量,经过Interface办法取得field接口类型变量的实在类型,能够理解为reflect.Value的逆操作
   // 在这儿,断语便是将一个接口类型的变量转化为time.Time,前提是后者有必要完成了前者的接口
   // 综上,这儿便是将field进行了类型转化
   if date, ok := field.Interface().(time.Time); ok {
      today := time.Now()
      if today.Year() > date.Year() || today.YearDay() > date.YearDay() {
         return false
      }
   }
   return true
}
func main() {
   route := gin.Default()
   // 注册自界说验证器
   if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
      v.RegisterValidation("bookabledate", bookableDate)
   }
   route.GET("/bookable", getBookable)
   route.Run(":8080")
}
func getBookable(c *gin.Context) {
   var b Booking
   if err := c.ShouldBindWith(&b, binding.Query); err == nil {
      c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"})
   } else {
      c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
   }
}

上面办法自界说了一种参数验证器。

项目结构参阅

├── conf #项目配置文件目录
│ └── config.toml #咱们能够挑选自己了解的配置文件办理工具包例如:toml、xml等等
├── controllers #操控器目录,按模块寄存操控器(或许叫操控器函数),必要的时分能够继续划分子目录。
│ ├── food.go
│ └── user.go
├── main.go #项目入口,这儿负责Gin结构的初始化,注册路由信息,相关操控器函数等。
├── models #模型目录,负责项目的数据存储部分,例如各个模块的Mysql表的读写模型。
│ ├── food.go
│ └── user.go
├── static #静态资源目录,包含Js,css,jpg等等,能够经过Gin结构配置,直接让用户拜访。
│ ├── css
│ ├── images
│ └── js
├── logs #日志文件目录,首要保存项目运转过程中发生的日志。
└── views #视图模板目录,寄存各个模块的视图模板,当然有些项目只有api,是不需求视图部分,能够疏忽这个目录
└── index.html

Gin结构运转方式

为便利调试,Gin 结构在运转的时分默认是debug方式,在操控台默认会打印出许多调试日志,上线的时分咱们需求封闭debug方式,改为release方式。

设置Gin结构运转方式:

  • 经过环境变量设置 export GIN_MODE=release

GIN_MODE环境变量,能够设置为debug或许release

  • 经过代码设置
在main函数,初始化gin结构的时分履行下面代码
// 设置 release方式
gin.SetMode(gin.ReleaseMode)
// 或许 设置debug方式
gin.SetMode(gin.DebugMode)

Gin怎么获取客户ip

route.GET("/ip", func(c *gin.Context) {
   // 获取用户IP
   ip := c.ClientIP()
   c.JSON(http.StatusBadRequest, gin.H{"ip": ip})
})

Gin处理恳求成果

以String类型呼应恳求

func (c *Context) String(code int, format string, values ...interface{})
c.String(200,"欢迎拜访%s, 你是%s", "tizi360.com!","最靓的仔!")

以Json格局呼应恳求

咱们开发api接口的时分常用的格局便是json,下面是回来json格局数据的比方

// User 界说
type User struct {
  Name  string `json:"name"` // 经过json标签界说struct字段转化成json字段的姓名。
  Email string `json:"email"`
}
// Handler 操控器
func(c *gin.Context) {
  //初始化user目标
  u := &User{
    Name:  "tizi365",
    Email: "tizi@tizi365.com",
  }
  //回来json数据
  //回来成果:{"name":"tizi365", "email":"tizi@tizi365.com"}
  c.JSON(200, u)
}

以xml格局呼应恳求

以文件方式呼应恳求

  c.FileAttachment("/var/www/1.jpg", "1.jpg")

设置http呼应头

func(c *gin.Context) {
  //设置http呼应 header, key/value办法,支撑设置多个header
  c.Header("site","tizi365")
}

Gin处理html模板

func main() {
    // 初始化gin目标
    router := gin.Default()
    // 首先加载templates目录下面的所有模版文件,模版文件扩展名随意
    router.LoadHTMLGlob("templates/*")
    // 绑定一个url路由 /index
    router.GET("/index", func(c *gin.Context) {
            // 经过HTML函数回来html代码
            // 第二个参数是模版文件姓名
            // 第三个参数是map类型,代表模版参数
            // gin.H 是map[string]interface{}类型的别名
            c.HTML(http.StatusOK, "index.html", gin.H{
                    "title": "Main website",
            })
    })
    // 发动http服务,而且绑定在8080端口
    router.Run(":8080")
}

7. Gin拜访静态资源文件

func main() {
    router := gin.Default()
    // 设置静态资源文件目录,而且绑定一个Url前缀
    // 静态资源文件目录:/var/www/tizi365/assets
    // /assets是拜访静态资源的url前缀
    // 例如:
    //   /assets/images/1.jpg 这个url文件,存储在/var/www/tizi365/assets/images/1.jpg
    router.Static("/assets", "/var/www/tizi365/assets")
    // 为单个静态资源文件,绑定url
    // 这儿的意思便是将/favicon.ico这个url,绑定到./resources/favicon.ico这个文件
    router.StaticFile("/favicon.ico", "./resources/favicon.ico")
    // Listen and serve on 0.0.0.0:8080
    router.Run(":8080")
}

Gin处理Cookie操作

package main
import (
   "fmt"
   "github.com/gin-gonic/gin"
)
func main() {
   router := gin.Default()
   // 设置cookie路由
   router.GET("/setCookie", func(c *gin.Context) {
      // 设置cookie
      c.SetCookie("site_cookie", "cookievalue", 3600, "/", "localhost", false, true)
   })
   // 取得cookie路由
   router.GET("/cookie", func(c *gin.Context) {
      data, err := c.Cookie("/cookie")
      fmt.Printf(data)
      if err != nil {
         // 直接回来cookie值
         c.String(200, data)
         return
      }
      c.String(200, "not found!")
   })
   //删去cookie路由
   router.GET("/removeCookie", func(c *gin.Context) {
      c.SetCookie("set_cookie", "cookievalue", -1, "/", "localhost", false, true)
      c.String(200, "删去cookie")
   })
   router.Run(":8080")
}

Gin文件上传

package main
// 导入gin包
import (
   "fmt"
   "github.com/gin-gonic/gin"
   "log"
   "net/http"
)
func main() {
   router := gin.Default()
   // 设置文件上传大小限制,默认是32m
   router.MaxMultipartMemory = 64 << 20 // 64 MiB
   router.POST("/upload", func(c *gin.Context) {
      // 获取上传文件,回来的是multipart.FileHeader目标,代表一个文件,里面包含了文件名之类的详细信息
      // file是表单字段姓名
      file, _ := c.FormFile("file")
      // 打印上传的文件名
      log.Println(file.Filename)
      // 将上传的文件,保存到./data/1111.jpg 文件中
      c.SaveUploadedFile(file, "./data/1111.jpg")
      c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
   })
   router.Run(":8080")
}

Gin中心件

在Gin结构中,中心件(Middleware)指的是能够阻拦http恳求-呼应生命周期的特别函数,在恳求-呼应生命周期中能够注册多个中心件,每个中心件履行不同的功用,一个中心履行完再轮到下一个中心件履行。

中心件的常见运用场景如下:

  • 恳求限速

  • api接口签名处理

  • 权限校验

  • 一致过错处理

Gin支撑设置大局中心件和针对路由分组设置中心件,设置大局中心件意思便是会阻拦所有恳求,针对分组路由设置中心件,意思便是仅对这个分组下的路由起作用。

 package main
// 导入gin包
import (
   "fmt"
   "github.com/gin-gonic/gin"
   "log"
   "time"
)
// 自界说个日志中心件
func Logger() gin.HandlerFunc {
   return func(c *gin.Context) {
      t := time.Now()
      // 能够经过上下文目标,设置一些依附在上下文目标里面的键/值数据
      c.Set("example", "12345")
      fmt.Printf("1. 履行中心件设置 \n")
      // 在这儿处理恳求到达操控器函数之前的逻辑
      // 调用下一个中心件,或许操控器处理函数,具体得看注册了多少个中心件。
      c.Next()
      fmt.Printf("4. 完成 履行中心件设置 \n")
      // 在这儿能够处理恳求回来给用户之前的逻辑
      latency := time.Since(t)
      log.Print(latency)
      // 例如,查询恳求状况吗
      status := c.Writer.Status()
      log.Println(status)
   }
}
func main() {
   r := gin.New()
   // 注册上面自界说的日志中心件
   r.Use(Logger())
   r.GET("/test", func(c *gin.Context) {
      // 查询咱们之前在日志中心件,注入的键值数据
      fmt.Printf("2. 开始履行事务逻辑 \n")
      example := c.MustGet("example").(string)
      // it would print: "12345"
      log.Println(example)
      fmt.Printf("3. 事务逻辑履行完成 \n")
   })
   // Listen and serve on 0.0.0.0:8080
   r.Run(":8080")
}

其输出成果如下:

1. 履行中心件设置
2. 开始履行事务逻辑 
2022/10/22 16:33:29 12345
3. 事务逻辑履行完成 
4. 完成 履行中心件设置 
2022/10/22 16:33:29 658.681s
2022/10/22 16:33:29 200

Gin 中心件类似 Node中的洋葱模型。

参阅链接

  • Go gin结构入门教程

  • golang web结构——gin运用教程