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() }
关于常用中间件分享
中间件推荐
会话控制
- Cookie介绍
- HTTP是无状态协议,服务器不能记录浏览器的访问状态,也就是说服务器不能区分两次请求是否由同一个客户端发出
- Cookie就是解决HTTP协议无状态的方案之一
- Cookie实际上就是服务器保存在浏览器的一段信息
- 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为必填字段" ] } }