Gin框架中間件&會話控制&參數(shù)驗證

全局中間件

全局中間件表示,所有的請求都經(jīng)過該函數(shù)

package main

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

//中間件
func MiddleWare() gin.HandlerFunc {
  return func(c *gin.Context) {
      //獲取當(dāng)前時間
      now := time.Now()
      fmt.Println("開始執(zhí)行中間件函數(shù)")
      c.Set("request", "中間件")
      status := c.Writer.Status()
      fmt.Println("中間件執(zhí)行完成======>", 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
控制臺輸出
開始執(zhí)行中間件函數(shù)
中間件執(zhí)行完成======> 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) {
      //獲取當(dāng)前時間
      now := time.Now()
      fmt.Println("開始執(zhí)行中間件函數(shù)")
      c.Set("request", "中間件")
      //執(zhí)行完函數(shù)
      c.Next()
      fmt.Println("函數(shù)執(zhí)行完成")
      //執(zhí)行完函數(shù)之后再執(zhí)行
      status := c.Writer.Status()
      fmt.Println("中間件執(zhí)行完成======>", 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) {
      //獲取當(dāng)前時間
      now := time.Now()
      fmt.Println("開始執(zhí)行中間件函數(shù)")
      c.Set("request", "中間件")
      //執(zhí)行完函數(shù)
      c.Next()
      fmt.Println("函數(shù)執(zhí)行完成")
      //執(zhí)行完函數(shù)之后再執(zhí)行
      status := c.Writer.Status()
      fmt.Println("中間件執(zhí)行完成======>", 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()
}

關(guān)于常用中間件分享
中間件推薦

會話控制

  1. Cookie介紹
    1. HTTP是無狀態(tài)協(xié)議,服務(wù)器不能記錄瀏覽器的訪問狀態(tài),也就是說服務(wù)器不能區(qū)分兩次請求是否由同一個客戶端發(fā)出
    2. Cookie就是解決HTTP協(xié)議無狀態(tài)的方案之一
    3. Cookie實際上就是服務(wù)器保存在瀏覽器的一段信息
    4. Cookie由服務(wù)器創(chuàng)建,并發(fā)送給瀏覽器,最終由瀏覽器保存
package main

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

func main() {
  // 1.創(chuàng)建路由
  // 默認(rèn)使用了2個中間件Logger(), Recovery()
  r := gin.Default()
  // 服務(wù)端要給客戶端cookie
  r.GET("cookie", func(c *gin.Context) {
      // 獲取客戶端是否攜帶cookie
      cookie, err := c.Cookie("key_cookie")
      if err != nil {
          cookie = "NotSet"
          // 給客戶端設(shè)置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和文件系統(tǒng)session以及基礎(chǔ)結(jié)構(gòu)。

主要功能是:

  • 簡單的API:將其用作設(shè)置簽名cookie的簡便方法
  • 內(nèi)置的后端可將session存儲在cookie或文件系統(tǒng)中
  • Flash消息:一直持續(xù)讀取session值
  • 切換session持久化和設(shè)置其他屬性的便捷方法
  • 旋轉(zhuǎn)身份驗證和加密密鑰的機(jī)制
  • 每個請求有多個session,即使使用不同的后端也是如此
  • 自定義session后端的接口和基礎(chǔ)結(jié)構(gòu):可以使用通用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)
}

參數(shù)驗證

用gin框架的數(shù)據(jù)驗證,可以不勇解析數(shù)據(jù),減少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
//自定義驗證函數(shù)
//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 {
   //定義一個預(yù)約的時間大于今天的時間
   CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
   //gtfield=CheckIn退出的時間大于預(yù)約的時間
   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)獲取參數(shù)值并且轉(zhuǎn)換為時間格式
   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 {
       //綁定第一個參數(shù)是驗證的函數(shù)第二個參數(shù)是自定義的驗證函數(shù)
       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"

多語言翻譯驗證

對于多業(yè)務(wù)系統(tǒng)可能存在特殊需求,手機(jī)端需要中文而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為必填字段"
        ]
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容