全局中間件
全局中間件表示,所有的請(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ì)話控制
- Cookie介紹
- HTTP是無狀態(tài)協(xié)議笨枯,服務(wù)器不能記錄瀏覽器的訪問狀態(tài)庸推,也就是說服務(wù)器不能區(qū)分兩次請(qǐng)求是否由同一個(gè)客戶端發(fā)出
- Cookie就是解決HTTP協(xié)議無狀態(tài)的方案之一
- Cookie實(shí)際上就是服務(wù)器保存在瀏覽器的一段信息
- 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為必填字段" ] } }