Gin框架中間件&會(huì)話控制&參數(shù)驗(yàn)證

全局中間件

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

package main

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

//中間件
func MiddleWare() gin.HandlerFunc {
  return func(c *gin.Context) {
      //獲取當(dāng)前時(shí)間
      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()
  //注冊(cè)中間件
  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
控制臺(tái)輸出
開始執(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)前時(shí)間
      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()
  //注冊(cè)中間件
  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)前時(shí)間
      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)于常用中間件分享
中間件推薦

會(huì)話控制

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

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

func main() {
  // 1.創(chuàng)建路由
  // 默認(rèn)使用了2個(gè)中間件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存儲(chǔ)在cookie或文件系統(tǒng)中
  • Flash消息:一直持續(xù)讀取session值
  • 切換session持久化和設(shè)置其他屬性的便捷方法
  • 旋轉(zhuǎn)身份驗(yàn)證和加密密鑰的機(jī)制
  • 每個(gè)請(qǐng)求有多個(gè)session酷含,即使使用不同的后端也是如此
  • 自定義session后端的接口和基礎(chǔ)結(jié)構(gòu):可以使用通用API檢索并批量保存來自不同商店的session。
package main

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

//初始化一個(gè)cookie存儲(chǔ)對(duì)象
//bzka是一個(gè)自己的密鑰
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) {
  // 獲取一個(gè)session對(duì)象哨毁,session-name是session的名字
  session, err := store.Get(r, "sessionName")
  if err != nil {
      http.Error(w, err.Error(), http.StatusInternalServerError)
      return
  }
  // 在session中存儲(chǔ)值
  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ù)驗(yàn)證

用gin框架的數(shù)據(jù)驗(yàn)證枫甲,可以不勇解析數(shù)據(jù),減少if else會(huì)簡潔許多

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()
}
//請(qǐng)求
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

自定義驗(yàn)證

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
//自定義驗(yàn)證函數(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()
  //自定義的校驗(yàn)方法注冊(cè)到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 {
   //定義一個(gè)預(yù)約的時(shí)間大于今天的時(shí)間
   CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
   //gtfield=CheckIn退出的時(shí)間大于預(yù)約的時(shí)間
   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)換為時(shí)間格式
   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()
   //注冊(cè)驗(yàn)證
   if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
       //綁定第一個(gè)參數(shù)是驗(yàn)證的函數(shù)第二個(gè)參數(shù)是自定義的驗(yàn)證函數(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àn)證

對(duì)于多業(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() {
  //注冊(cè)翻譯器
  zh := zh.New()
  uni = ut.New(zh, zh)
  trans, _ = uni.GetTranslator("zh")
  //獲取gin的校驗(yàn)器
  validate := binding.Validator.Engine().(*validator.Validate)
  //中文版本
  //zh_translations.RegisterDefaultTranslations(validate, trans)
  //英文版本
  en_translations.RegisterDefaultTranslations(validate, trans)
}

//Translate 翻譯錯(cuò)誤信息
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() {
  //初始化注冊(cè)器
  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)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末话浇,一起剝皮案震驚了整個(gè)濱河市脏毯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌幔崖,老刑警劉巖食店,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異赏寇,居然都是意外死亡吉嫩,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門嗅定,熙熙樓的掌柜王于貴愁眉苦臉地迎上來自娩,“玉大人,你說我怎么就攤上這事渠退∶ηǎ” “怎么了?”我有些...
    開封第一講書人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵碎乃,是天一觀的道長姊扔。 經(jīng)常有香客問我,道長梅誓,這世上最難降的妖魔是什么旱眯? 我笑而不...
    開封第一講書人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任晨川,我火速辦了婚禮,結(jié)果婚禮上删豺,老公的妹妹穿的比我還像新娘共虑。我一直安慰自己,他們只是感情好呀页,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開白布妈拌。 她就那樣靜靜地躺著,像睡著了一般蓬蝶。 火紅的嫁衣襯著肌膚如雪尘分。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,929評(píng)論 1 290
  • 那天丸氛,我揣著相機(jī)與錄音培愁,去河邊找鬼。 笑死缓窜,一個(gè)胖子當(dāng)著我的面吹牛定续,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播禾锤,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼私股,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了恩掷?” 一聲冷哼從身側(cè)響起倡鲸,我...
    開封第一講書人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎黄娘,沒想到半個(gè)月后峭状,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逼争,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年优床,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片氮凝。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡羔巢,死狀恐怖望忆,靈堂內(nèi)的尸體忽然破棺而出罩阵,到底是詐尸還是另有隱情,我是刑警寧澤启摄,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布稿壁,位于F島的核電站,受9級(jí)特大地震影響歉备,放射性物質(zhì)發(fā)生泄漏傅是。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望喧笔。 院中可真熱鬧帽驯,春花似錦、人聲如沸书闸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽浆劲。三九已至嫌术,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間牌借,已是汗流浹背度气。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留膨报,地道東北人磷籍。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像丙躏,于是被迫代替她去往敵國和親择示。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容