Go语言学习路

Gin框架中间件&会话控制&参数验证

2021-08-13  本文已影响0人  TZX_0710

全局中间件

全局中间件表示,所有的请求都经过该函数

package main

import (
  "fmt"
  "github.com/gin-gonic/gin"
  "time"
)

//中间件
func MiddleWare() gin.HandlerFunc {
  return func(c *gin.Context) {
      //获取当前时间
      now := time.Now()
      fmt.Println("开始执行中间件函数")
      c.Set("request", "中间件")
      status := c.Writer.Status()
      fmt.Println("中间件执行完成======>", status)
      t2 := time.Since(now)
      fmt.Println("time:", t2)
  }
}

func main() {
  r := gin.Default()
  //注册中间件
  r.Use(MiddleWare())
  r.GET("/middle", func(context *gin.Context) {
      req, _ := context.Get("request")
      context.JSON(200, gin.H{"request": req})
  })
  r.Run()
}
curl http://127.0.0.1:8080/middle
控制台输出
开始执行中间件函数
中间件执行完成======> 200
C:\Users\dell>curl http://127.0.0.1:8080/middle
{"request":"中间件"}

Next()方法

package main

import (
  "fmt"
  "github.com/gin-gonic/gin"
  "time"
)

//中间件
func MiddleWare() gin.HandlerFunc {
  return func(c *gin.Context) {
      //获取当前时间
      now := time.Now()
      fmt.Println("开始执行中间件函数")
      c.Set("request", "中间件")
      //执行完函数
      c.Next()
      fmt.Println("函数执行完成")
      //执行完函数之后再执行
      status := c.Writer.Status()
      fmt.Println("中间件执行完成======>", status)
      t2 := time.Since(now)
      fmt.Println("time:", t2)
  }
}

func main() {
  r := gin.Default()
  //注册中间件
  r.Use(MiddleWare())
  r.GET("/middle", func(context *gin.Context) {
      req, _ := context.Get("request")
      context.JSON(200, gin.H{"request": req})
  })
  r.Run()
}

局部中间件

package main

import (
  "fmt"
  "github.com/gin-gonic/gin"
  "time"
)

//局部中间件
func MiddleWare() gin.HandlerFunc {
  return func(c *gin.Context) {
      //获取当前时间
      now := time.Now()
      fmt.Println("开始执行中间件函数")
      c.Set("request", "中间件")
      //执行完函数
      c.Next()
      fmt.Println("函数执行完成")
      //执行完函数之后再执行
      status := c.Writer.Status()
      fmt.Println("中间件执行完成======>", status)
      t2 := time.Since(now)
      fmt.Println("time:", t2)
  }
}

func main() {
  r := gin.Default()
  //局部中间件
  r.GET("/middle",MiddleWare(), func(context *gin.Context) {
      req, _ := context.Get("request")
      context.JSON(200, gin.H{"request": req})
  })
  r.Run()
}

关于常用中间件分享
中间件推荐

会话控制

  1. Cookie介绍
    1. HTTP是无状态协议,服务器不能记录浏览器的访问状态,也就是说服务器不能区分两次请求是否由同一个客户端发出
    2. Cookie就是解决HTTP协议无状态的方案之一
    3. Cookie实际上就是服务器保存在浏览器的一段信息
    4. Cookie由服务器创建,并发送给浏览器,最终由浏览器保存
package main

import (
  "fmt"
  "github.com/gin-gonic/gin"
)

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

Sessions

gorilla/sessions为自定义session后端提供cookie和文件系统session以及基础结构。

主要功能是:

  • 简单的API:将其用作设置签名cookie的简便方法
  • 内置的后端可将session存储在cookie或文件系统中
  • Flash消息:一直持续读取session值
  • 切换session持久化和设置其他属性的便捷方法
  • 旋转身份验证和加密密钥的机制
  • 每个请求有多个session,即使使用不同的后端也是如此
  • 自定义session后端的接口和基础结构:可以使用通用API检索并批量保存来自不同商店的session。
package main

import (
  "fmt"
  "github.com/gorilla/sessions"
  "net/http"
)

//初始化一个cookie存储对象
//bzka是一个自己的密钥
var store = sessions.NewCookieStore([]byte("bzka"))

func main() {
  http.HandleFunc("/save", SaveSession)
  http.HandleFunc("/get", GetSession)
  err := http.ListenAndServe(":8080", nil)
  if err != nil {
      fmt.Println("HTTP server failed,err:", err)
      return
  }
}

func SaveSession(w http.ResponseWriter, r *http.Request) {
  // 获取一个session对象,session-name是session的名字
  session, err := store.Get(r, "sessionName")
  if err != nil {
      http.Error(w, err.Error(), http.StatusInternalServerError)
      return
  }
  // 在session中存储值
  session.Values["foo"] = "bar"
  session.Values[42] = 43
  // 保存更改
  session.Save(r, w)
}
func GetSession(w http.ResponseWriter, r *http.Request) {
  session, err := store.Get(r, "sessionName")
  if err != nil {
      http.Error(w, err.Error(), http.StatusInternalServerError)
      return
  }
  foo := session.Values["foo"]
  fmt.Println(foo)
}

参数验证

用gin框架的数据验证,可以不勇解析数据,减少if else会简洁许多

package main

import (
  "fmt"
  "time"

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

//Person ..
type Person struct {
  //不能为空并且大于10
  Age      int       `form:"age" binding:"required,gt=10"`
  Name     string    `form:"name" binding:"required"`
  Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"`
}

func main() {
  r := gin.Default()
  r.GET("/5lmh", func(c *gin.Context) {
      var person Person
      if err := c.ShouldBind(&person); err != nil {
          c.String(500, fmt.Sprint(err))
          return
      }
      c.String(200, fmt.Sprintf("%#v", person))
  })
  r.Run()
}
//请求
http://localhost:8080/5lmh?age=8&name=枯藤&birthday=2006-01-02
result
Key: 'Person.Age' Error:Field validation for 'Age' failed on the 'gt' tag

自定义验证

package main

import (
  "github.com/gin-gonic/gin"
  "github.com/gin-gonic/gin/binding"
  "github.com/go-playground/validator/v10"
  "net/http"
  "time"
)

//Person ..
type Person struct {
  //不能为空并且大于10
  Age      int       `form:"age" binding:"required,gt=10"`
  Name     string    `form:"name" binding:"NotNullAndAdmin"`
  Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"`
}

//官方示例https://pkg.go.dev/gopkg.in/go-playground/validator.v8?utm_source=godoc#hdr-Custom_Functions
//自定义验证函数
//v8框架如下写法
//func nameNotNullAndRoot(v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
// if value, ok := field.Interface().(string); ok {
//     // 字段不能为空,并且不等于  admin
//     return value != "" && !("root" == value)
// }
// return true
//}

//v8以上框架写法
func nameNotNullAndRoot(fl validator.FieldLevel) bool {
  name := fl.Field().Interface().(string)
  return name != "" && !("root" == name)
}
func main() {
  r := gin.Default()
  //自定义的校验方法注册到validator中
  if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
      // 这里的 key 和 fn 可以不一样最终在 struct 使用的是 key
      v.RegisterValidation("NotNullAndAdmin", nameNotNullAndRoot)
  }
  r.GET("/name", func(context *gin.Context) {
      var person Person
      if e := context.ShouldBind(&person); e == nil {
          context.String(http.StatusOK, "%v", person)
      } else {
          context.String(http.StatusOK, "person bind err:%v", e.Error())
      }
  })
  r.Run()
}
person bind err:Key: 'Person.Name' Error:Field validation for 'Name' failed on the 'NotNullAndAdmin' tag

package main

import (
   "net/http"
   "reflect"
   "time"

   "github.com/gin-gonic/gin"
   "github.com/gin-gonic/gin/binding"
   "gopkg.in/go-playground/validator.v8"
)

// Booking contains binded and validated data.
type Booking struct {
   //定义一个预约的时间大于今天的时间
   CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
   //gtfield=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.Interface().(time.Time)获取参数值并且转换为时间格式
   if date, ok := field.Interface().(time.Time); ok {
       today := time.Now()
       if today.Unix() > date.Unix() {
           return false
       }
   }
   return true
}

func main() {
   route := gin.Default()
   //注册验证
   if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
       //绑定第一个参数是验证的函数第二个参数是自定义的验证函数
       v.RegisterValidation("bookabledate", bookableDate)
   }

   route.GET("/5lmh", getBookable)
   route.Run()
}

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()})
   }
}

// curl -X GET "http://localhost:8080/5lmh?check_in=2019-11-07&check_out=2019-11-20"
// curl -X GET "http://localhost:8080/5lmh?check_in=2019-09-07&check_out=2019-11-20"
// curl -X GET "http://localhost:8080/5lmh?check_in=2019-11-07&check_out=2019-11-01"

多语言翻译验证

对于多业务系统可能存在特殊需求,手机端需要中文而PC端需要英文,有的则包含自定义信息

package main

import (
  "github.com/gin-gonic/gin"
  "github.com/gin-gonic/gin/binding"
  "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"
)

var (
  uni      *ut.UniversalTranslator
  validate *validator.Validate
  trans    ut.Translator
)

type Person struct {
  //不能为空并且大于10
  Age int `form:"age" binding:"required,gt=10"`
}

func Init() {
  //注册翻译器
  zh := zh.New()
  uni = ut.New(zh, zh)
  trans, _ = uni.GetTranslator("zh")
  //获取gin的校验器
  validate := binding.Validator.Engine().(*validator.Validate)
  //中文版本
  //zh_translations.RegisterDefaultTranslations(validate, trans)
  //英文版本
  en_translations.RegisterDefaultTranslations(validate, trans)
}

//Translate 翻译错误信息
func Translate(err error) map[string][]string {
  var result = make(map[string][]string)
  errors := err.(validator.ValidationErrors)
  for _, err := range errors {
      result[err.Field()] = append(result[err.Field()], err.Translate(trans))
  }
  return result
}
func main() {
  //初始化注册器
  Init()
  r := gin.Default()
  r.GET("zhV", func(context *gin.Context) {
      var person Person
      if err := context.ShouldBind(&person); err == nil {
          context.JSON(200, gin.H{"message": "Success"})
      } else {
          context.JSON(500, gin.H{"message": Translate(err)})
      }
  })
  r.Run()
}

http://localhost:8080/zhV
{
    "message": {
        "Age": [
            "Age为必填字段"
        ]
    }
}
上一篇 下一篇

猜你喜欢

热点阅读