Gin框架

安装gin

下载并安装gin

go get -u github.com/gin-gonic/gin 如果安装失败,首先执行命令:go env -w GOSUMDB=off 如果不可以 ,切换国内源: go env -w GO111MODULE=on go env -w GOPROXY=https://goproxy.io,direct 将gin引入到代码 import “github.com/gin-gonic/gin”

(可选)导入net/http。例如,如果使用常量,则需要这样做http.StatusOK。

import “net/http”

学习网址:http://www.topgoer.com/gin%E6%A1%86%E6%9E%B6/gin%E8%B7%AF%E7%94%B1/%E8%A1%A8%E5%8D%95%E5%8F%82%E6%95%B0.html

开始第一个gin

  1. package main
  2. import (
  3. "net/http"
  4. "github.com/gin-gonic/gin"
  5. )
  6. func main() {
  7. // 1.创建路由
  8. r := gin.Default()
  9. // 2.绑定路由规则,执行的函数
  10. // gin.Context,封装了request和response
  11. r.GET("/", func(c *gin.Context) {
  12. c.String(http.StatusOK, "hello World!")
  13. })
  14. // 3.监听端口,默认在8080
  15. // Run("里面不指定端口号默认为8080")
  16. r.Run(":7711")
  17. }

游览器访问:http://localhost:7711/

路由

基本路由

gin框架中采用的路由库是基于httprouter的,地址为:https://github.com/julienschmidt/httprouter

r.POST(“/mpost”)

r.PUT(“/mput”)

r.DELETE(“/mdelete”)

API参数

  • 可以通过Context的Param方法来获取API参数
  • localhost:8000/xxx/zhangsan

    1. ![](https://cdn.nlark.com/yuque/0/2021/png/21374849/1620886474632-137a0d8e-fb69-49d8-92f0-e09e7151a322.png)

URL参数

  • URL参数可以通过DefaultQuery()或Query()方法获取
  • DefaultQuery()若参数不村则,返回默认值,Query()若不存在,返回空串
  • API ? name=zs

Gin框架 - 图1

表单参数

  • 表单传输为post请求,http常见的传输格式为四种:
    • application/json
    • application/x-www-form-urlencoded
    • application/xml
    • multipart/form-data
  • 表单参数可以通过PostForm()方法获取,该方法默认解析的是x-www-form-urlencoded或from-data格式的参数

PostForm()

Gin框架 - 图2

BindJSON()

用作application/json参数请求

关键点:**结构体名称不能与请求json的字段名一致**

Gin框架 - 图3

上传文件

http://www.topgoer.com/gin%E6%A1%86%E6%9E%B6/gin%E8%B7%AF%E7%94%B1/routesgroup.html

routers group

  • routes group是为了管理一些相同的URL

Gin框架 - 图4

gin数据解析和绑定

  1. // 定义接收数据的结构体
  2. type Login struct {
  3. // binding:"required"修饰的字段,若接收为空值,则报错,是必须字段
  4. User string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"`
  5. Pssword string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"`
  6. }
  7. // 1.创建路由
  8. // 默认使用了2个中间件Logger(), Recovery()
  9. r := gin.Default()
  10. // 将request的body中的数据,自动按照json格式解析到结构体
  11. if err := c.ShouldBindJSON(&json); err != nil {
  12. // 返回错误信息
  13. // gin.H封装了生成json数据的工具
  14. c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
  15. return
  16. }
  17. // Bind()默认解析并绑定form格式
  18. // 根据请求头中content-type自动推断
  19. if err := c.Bind(&form); err != nil {
  20. c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
  21. return
  22. }
  23. // Bind()默认解析并绑定form格式
  24. // 根据请求头中content-type自动推断
  25. if err := c.ShouldBindUri(&login); err != nil {
  26. c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
  27. return
  28. }

gin渲染

各种数据格式的响应

  1. // 1.json
  2. r.GET("/someJSON", func(c *gin.Context) {
  3. c.JSON(200, gin.H{"message": "someJSON", "status": 200})
  4. })
  5. // 2. 结构体响应
  6. r.GET("/someStruct", func(c *gin.Context) {
  7. var msg struct {
  8. Name string
  9. Message string
  10. Number int
  11. }
  12. msg.Name = "root"
  13. msg.Message = "message"
  14. msg.Number = 123
  15. c.JSON(200, msg)
  16. })
  17. // 3.XML
  18. r.GET("/someXML", func(c *gin.Context) {
  19. c.XML(200, gin.H{"message": "abc"})
  20. })
  21. // 4.YAML响应
  22. r.GET("/someYAML", func(c *gin.Context) {
  23. c.YAML(200, gin.H{"name": "zhangsan"})
  24. })
  25. // 5.protobuf格式,谷歌开发的高效存储读取的工具
  26. // 数组?切片?如果自己构建一个传输格式,应该是什么格式?
  27. r.GET("/someProtoBuf", func(c *gin.Context) {
  28. reps := []int64{int64(1), int64(2)}
  29. // 定义数据
  30. label := "label"
  31. // 传protobuf格式数据
  32. data := &protoexample.Test{
  33. Label: &label,
  34. Reps: reps,
  35. }
  36. c.ProtoBuf(200, data)
  37. })

html模板渲染

gin支持加载HTML模板, 然后根据模板参数进行配置并返回相应的数据,本质上就是字符串替换

LoadHTMLGlob()方法可以加载模板文件

  • 如果你需要引入静态文件需要定义一个静态文件目录
  1. r.Static("/assets", "./assets")
  2. // 重定向
  3. c.Redirect(http.StatusMovedPermanently, "http://www.5lmh.com")
  4. // 1.异步
  5. r.GET("/long_async", func(c *gin.Context) {
  6. // 需要搞一个副本
  7. copyContext := c.Copy()
  8. // 异步处理
  9. go func() {
  10. time.Sleep(3 * time.Second)
  11. log.Println("异步执行:" + copyContext.Request.URL.Path)
  12. }()
  13. })
  14. // 2.同步
  15. r.GET("/long_sync", func(c *gin.Context) {
  16. time.Sleep(3 * time.Second)
  17. log.Println("同步执行:" + c.Request.URL.Path)
  18. })

中间件

全局中间件

Gin框架 - 图5

使用:r.Use(MiddleWare())

取值:req, _ := c.Get(“request”)

Next()方法

Gin框架 - 图6

局部中间件

Gin框架 - 图7

计算程序用时

Gin框架 - 图8

Cookie

  1. func main() {
  2. // 1.创建路由
  3. // 默认使用了2个中间件Logger(), Recovery()
  4. r := gin.Default()
  5. // 服务端要给客户端cookie
  6. r.GET("cookie", func(c *gin.Context) {
  7. // 获取客户端是否携带cookie
  8. cookie, err := c.Cookie("key_cookie")
  9. if err != nil {
  10. cookie = "NotSet"
  11. // 给客户端设置cookie
  12. // maxAge int, 单位为秒
  13. // path,cookie所在目录
  14. // domain string,域名
  15. // secure 是否智能通过https访问
  16. // httpOnly bool 是否允许别人通过js获取自己的cookie
  17. c.SetCookie("key_cookie", "value_cookie", 60, "/",
  18. "localhost", false, true)
  19. }
  20. fmt.Printf("cookie的值是: %s\n", cookie)
  21. })
  22. r.Run(":8000")
  23. }

http://www.topgoer.com/gin%E6%A1%86%E6%9E%B6/%E5%8F%82%E6%95%B0%E9%AA%8C%E8%AF%81/%E7%BB%93%E6%9E%84%E4%BD%93%E9%AA%8C%E8%AF%81.html

参数验证

结构体验证

  1. type Person struct {
  2. //不能为空并且大于10
  3. Age int `form:"age" binding:"required,gt=10"`
  4. Name string `form:"name" binding:"required"`
  5. Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"`
  6. }
  7. if err := c.ShouldBind(&person); err != nil {
  8. c.String(500, fmt.Sprint(err))
  9. return
  10. }

自定义验证

  1. package main
  2. import (
  3. "net/http"
  4. "reflect"
  5. "github.com/gin-gonic/gin"
  6. "github.com/gin-gonic/gin/binding"
  7. "gopkg.in/go-playground/validator.v8"
  8. )
  9. /*
  10. 对绑定解析到结构体上的参数,自定义验证功能
  11. 比如我们要对 name 字段做校验,要不能为空,并且不等于 admin ,类似这种需求,就无法 binding 现成的方法
  12. 需要我们自己验证方法才能实现 官网示例(https://godoc.org/gopkg.in/go-playground/validator.v8#hdr-Custom_Functions)
  13. 这里需要下载引入下 gopkg.in/go-playground/validator.v8
  14. */
  15. type Person struct {
  16. Age int `form:"age" binding:"required,gt=10"`
  17. // 2、在参数 binding 上使用自定义的校验方法函数注册时候的名称
  18. Name string `form:"name" binding:"NotNullAndAdmin"`
  19. Address string `form:"address" binding:"required"`
  20. }
  21. // 1、自定义的校验方法
  22. func nameNotNullAndAdmin(v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
  23. if value, ok := field.Interface().(string); ok {
  24. // 字段不能为空,并且不等于 admin
  25. return value != "" && !("5lmh" == value)
  26. }
  27. return true
  28. }
  29. func main() {
  30. r := gin.Default()
  31. // 3、将我们自定义的校验方法注册到 validator中
  32. if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
  33. // 这里的 key 和 fn 可以不一样最终在 struct 使用的是 key
  34. v.RegisterValidation("NotNullAndAdmin", nameNotNullAndAdmin)
  35. }
  36. /*
  37. curl -X GET "http://127.0.0.1:8080/testing?name=&age=12&address=beijing"
  38. curl -X GET "http://127.0.0.1:8080/testing?name=lmh&age=12&address=beijing"
  39. curl -X GET "http://127.0.0.1:8080/testing?name=adz&age=12&address=beijing"
  40. */
  41. r.GET("/5lmh", func(c *gin.Context) {
  42. var person Person
  43. if e := c.ShouldBind(&person); e == nil {
  44. c.String(http.StatusOK, "%v", person)
  45. } else {
  46. c.String(http.StatusOK, "person bind err:%v", e.Error())
  47. }
  48. })
  49. r.Run()
  50. }

示例2:

  1. package main
  2. import (
  3. "net/http"
  4. "reflect"
  5. "time"
  6. "github.com/gin-gonic/gin"
  7. "github.com/gin-gonic/gin/binding"
  8. "gopkg.in/go-playground/validator.v8"
  9. )
  10. // Booking contains binded and validated data.
  11. type Booking struct {
  12. //定义一个预约的时间大于今天的时间
  13. CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
  14. //gtfield=CheckIn退出的时间大于预约的时间
  15. CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
  16. }
  17. func bookableDate(
  18. v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value,
  19. field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string,
  20. ) bool {
  21. //field.Interface().(time.Time)获取参数值并且转换为时间格式
  22. if date, ok := field.Interface().(time.Time); ok {
  23. today := time.Now()
  24. if today.Unix() > date.Unix() {
  25. return false
  26. }
  27. }
  28. return true
  29. }
  30. func main() {
  31. route := gin.Default()
  32. //注册验证
  33. if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
  34. //绑定第一个参数是验证的函数第二个参数是自定义的验证函数
  35. v.RegisterValidation("bookabledate", bookableDate)
  36. }
  37. route.GET("/5lmh", getBookable)
  38. route.Run()
  39. }
  40. func getBookable(c *gin.Context) {
  41. var b Booking
  42. if err := c.ShouldBindWith(&b, binding.Query); err == nil {
  43. c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"})
  44. } else {
  45. c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
  46. }
  47. }

多语言翻译验证

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gin-gonic/gin"
  5. "github.com/go-playground/locales/en"
  6. "github.com/go-playground/locales/zh"
  7. "github.com/go-playground/locales/zh_Hant_TW"
  8. ut "github.com/go-playground/universal-translator"
  9. "gopkg.in/go-playground/validator.v9"
  10. en_translations "gopkg.in/go-playground/validator.v9/translations/en"
  11. zh_translations "gopkg.in/go-playground/validator.v9/translations/zh"
  12. zh_tw_translations "gopkg.in/go-playground/validator.v9/translations/zh_tw"
  13. )
  14. var (
  15. Uni *ut.UniversalTranslator
  16. Validate *validator.Validate
  17. )
  18. type User struct {
  19. Username string `form:"user_name" validate:"required"`
  20. Tagline string `form:"tag_line" validate:"required,lt=10"`
  21. Tagline2 string `form:"tag_line2" validate:"required,gt=1"`
  22. }
  23. func main() {
  24. en := en.New()
  25. zh := zh.New()
  26. zh_tw := zh_Hant_TW.New()
  27. Uni = ut.New(en, zh, zh_tw)
  28. Validate = validator.New()
  29. route := gin.Default()
  30. route.GET("/5lmh", startPage)
  31. route.POST("/5lmh", startPage)
  32. route.Run(":8080")
  33. }
  34. func startPage(c *gin.Context) {
  35. //这部分应放到中间件中
  36. locale := c.DefaultQuery("locale", "zh")
  37. trans, _ := Uni.GetTranslator(locale)
  38. switch locale {
  39. case "zh":
  40. zh_translations.RegisterDefaultTranslations(Validate, trans)
  41. break
  42. case "en":
  43. en_translations.RegisterDefaultTranslations(Validate, trans)
  44. break
  45. case "zh_tw":
  46. zh_tw_translations.RegisterDefaultTranslations(Validate, trans)
  47. break
  48. default:
  49. zh_translations.RegisterDefaultTranslations(Validate, trans)
  50. break
  51. }
  52. //自定义错误内容
  53. Validate.RegisterTranslation("required", trans, func(ut ut.Translator) error {
  54. return ut.Add("required", "{0} must have a value!", true) // see universal-translator for details
  55. }, func(ut ut.Translator, fe validator.FieldError) string {
  56. t, _ := ut.T("required", fe.Field())
  57. return t
  58. })
  59. //这块应该放到公共验证方法中
  60. user := User{}
  61. c.ShouldBind(&user)
  62. fmt.Println(user)
  63. err := Validate.Struct(user)
  64. if err != nil {
  65. errs := err.(validator.ValidationErrors)
  66. sliceErrs := []string{}
  67. for _, e := range errs {
  68. sliceErrs = append(sliceErrs, e.Translate(trans))
  69. }
  70. c.String(200, fmt.Sprintf("%#v", sliceErrs))
  71. }
  72. c.String(200, fmt.Sprintf("%#v", "user"))
  73. }

其它

日志文件

Gin框架 - 图9

Air实时加载

安装:go get -u github.com/cosmtrek/air

直接运行:可以直接使用 air 运行

  1. # 首先在当前目录下查找 `.air.conf`配置文件,如果找不到就使用默认的
  2. air -c .air.conf