全局中间件
全局中间件表示,所有的请求都经过该函数
```go 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”:”中间件”}
<a name="11d4f518"></a>#### Next()方法> ```gopackage mainimport ("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()}
局部中间件
```go 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() }
> 关于常用中间件分享<br />[中间件推荐](https://hub.fastgit.org/gin-gonic/contrib/blob/master/README.md)<a name="69832669"></a>#### 会话控制> 1. Cookie介绍> 1. HTTP是无状态协议,服务器不能记录浏览器的访问状态,也就是说服务器不能区分两次请求是否由同一个客户端发出> 2. Cookie就是解决HTTP协议无状态的方案之一> 3. Cookie实际上就是服务器保存在浏览器的一段信息> 4. Cookie由服务器创建,并发送给浏览器,最终由浏览器保存> ```gopackage mainimport ("fmt""github.com/gin-gonic/gin")func main() {// 1.创建路由// 默认使用了2个中间件Logger(), Recovery()r := gin.Default()// 服务端要给客户端cookier.GET("cookie", func(c *gin.Context) {// 获取客户端是否携带cookiecookie, err := c.Cookie("key_cookie")if err != nil {cookie = "NotSet"// 给客户端设置cookie// maxAge int, 单位为秒// path,cookie所在目录// domain string,域名// secure 是否智能通过https访问// httpOnly bool 是否允许别人通过js获取自己的cookiec.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。
```go 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) }
<a name="186ffcaf"></a>#### 参数验证> 用gin框架的数据验证,可以不勇解析数据,减少if else会简洁许多> ```gopackage mainimport ("fmt""time""github.com/gin-gonic/gin")//Person ..type Person struct {//不能为空并且大于10Age 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 Personif 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-02resultKey: 'Person.Age' Error:Field validation for 'Age' failed on the 'gt' tag
自定义验证
```go 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“
<a name="820c26d4"></a>#### 多语言翻译验证> 对于多业务系统可能存在特殊需求,手机端需要中文而PC端需要英文,有的则包含自定义信息> ```gopackage mainimport ("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.UniversalTranslatorvalidate *validator.Validatetrans ut.Translator)type Person struct {//不能为空并且大于10Age 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 Personif 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为必填字段"]}}
