r.GET(“/benchmark”, before, main,after)
使用中间件
func main() {// 创建一个默认的没有任何中间件的路由r := gin.New()// 全局中间件// Logger 中间件将写日志到 gin.DefaultWriter ,即使你设置 GIN_MODE=release 。// 默认 gin.DefaultWriter = os.Stdoutr.Use(gin.Logger())// Recovery 中间件从任何 panic 恢复,如果出现 panic,它会写一个 500 错误。r.Use(gin.Recovery())// 每个路由的中间件, 你能添加任意数量的中间件r.GET("/benchmark", MyBenchLogger(), benchEndpoint)// 授权组// authorized := r.Group("/", AuthRequired())// 也可以这样:authorized := r.Group("/")// 每个组的中间件! 在这个实例中,我们只需要在 "authorized" 组中// 使用自定义创建的 AuthRequired() 中间件authorized.Use(AuthRequired()){authorized.POST("/login", loginEndpoint)authorized.POST("/submit", submitEndpoint)authorized.POST("/read", readEndpoint)// nested grouptesting := authorized.Group("testing")testing.GET("/analytics", analyticsEndpoint)}// 监听并服务于 0.0.0.0:8080r.Run(":8080")}
自定义中间件
func Logger() gin.HandlerFunc {return func(c *gin.Context) {t := time.Now()// 设置简单的变量c.Set("example", "12345")// 在请求之前c.Next()// 在请求之后latency := time.Since(t)log.Print(latency)// 记录我们的访问状态status := c.Writer.Status()log.Println(status)}}func main() {r := gin.New()r.Use(Logger())r.GET("/test", func(c *gin.Context) {example := c.MustGet("example").(string)// 它将打印: "12345"log.Println(example)})// 监听并服务于 0.0.0.0:8080r.Run(":8080")}
在中间件中使用协程
在一个中间件或处理器中启动一个新的协成时,你 不应该 使用它里面的原始的 context ,只能去使用它的只读副本。
func main() {r := gin.Default()r.GET("/long_async", func(c *gin.Context) {// 创建在协成中使用的副本cCp := c.Copy()go func() {// 使用 time.Sleep() 休眠 5 秒,模拟一个用时长的任务。time.Sleep(5 * time.Second)// 注意,你使用的是复制的 context "cCp" ,重要log.Println("Done! in path " + cCp.Request.URL.Path)}()})r.GET("/long_sync", func(c *gin.Context) {// 使用 time.Sleep() 休眠 5 秒,模拟一个用时长的任务。time.Sleep(5 * time.Second)// 因为我们没有使用协成,我们不需要复制 contextlog.Println("Done! in path " + c.Request.URL.Path)})// 监听并服务于 0.0.0.0:8080r.Run(":8080")}
使用 BasicAuth () 中间件
// 模拟一些私有的数据var secrets = gin.H{"foo": gin.H{"email": "foo@bar.com", "phone": "123433"},"austin": gin.H{"email": "austin@example.com", "phone": "666"},"lena": gin.H{"email": "lena@guapa.com", "phone": "523443"},}func main() {r := gin.Default()// 在组中使用 gin.BasicAuth() 中间件// gin.Accounts 是 map[string]string 的快捷方式authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{"foo": "bar","austin": "1234","lena": "hello2","manu": "4321",}))// /admin/secrets 结尾// 点击 "localhost:8080/admin/secretsauthorized.GET("/secrets", func(c *gin.Context) {// 获取 user, 它是由 BasicAuth 中间件设置的user := c.MustGet(gin.AuthUserKey).(string)if secret, ok := secrets[user]; ok {c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})} else {c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("})}})// 监听并服务于 0.0.0.0:8080r.Run(":8080")}
如何写入日志文件
func main() {// 禁用控制台颜色,当你将日志写入到文件的时候,你不需要控制台颜色。gin.DisableConsoleColor()// 写入日志的文件f, _ := os.Create("gin.log")gin.DefaultWriter = io.MultiWriter(f)// 如果你需要同时写入日志文件和控制台上显示,使用下面代码// gin.DefaultWriter = io.MultiWriter(f, os.Stdout)router := gin.Default()router.GET("/ping", func(c *gin.Context) {c.String(200, "pong")})router.Run(":8080")}
中间键扩展:不拦截自定请求
package middleimport ("github.com/gin-gonic/gin""strings")type SkipFunc func (c *gin.Context) bool//注意 传过来的path,前面加/func AllowPathSkip(path ...string) SkipFunc {return func(c *gin.Context) bool {thisPath := c.Request.URL.Pathfor _,v := range path{if len(path) >0 || strings.HasPrefix(thisPath,v){return true}}return false}}func MyMiddle(Skip SkipFunc) gin.HandlerFunc{return func(context *gin.Context) {if Skip(context){context.Next()return}// 下面走正常的中间件逻辑}}
优化,每次循环肯定会慢,关键点在于使用map来存住不走该中间键的path
package middleimport ("github.com/gin-gonic/gin")type SkipFunc func (c *gin.Context) bool//注意 传过来的path,前面加/func AllowPathSkip(path ...string) SkipFunc {var skip map[string]struct{}if length := len(path); length > 0 {skip = make(map[string]struct{}, length)for _,v := range path{skip[v] = struct{}{}}}return func(c *gin.Context) bool {if _,ok := skip[c.Request.URL.Path];ok{return true}return false}}func MyMiddle(Skip SkipFunc) gin.HandlerFunc{return func(context *gin.Context) {if Skip(context){ // 该请求是否需要跳过context.Next()return}// 下面走正常的中间件逻辑}}
跨域
// 处理跨域请求,支持options访问func Cors() gin.HandlerFunc {return func(c *gin.Context) {method := c.Request.Methodc.Header("Access-Control-Allow-Origin", "*") //必选项//动态允许Allow-Origin,解决"*"不能与Access-Control-Allow-Credentials为true共存的问题//ctx.Header("Access-Control-Allow-Origin",ctx.GetHeader("Origin"))//拿到跨域请求中Header的Token字段,不能用"*"c.Header("Access-Control-Allow-Headers", "Token,SessionId,Content-Type")c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS,PUT,DELETE")c.Header("Access-Control-Allow-Credentials", "true")// 能够让客户端的js读取到Headerc.Header("Access-Control-Expose-Headers", "X-Powered-By,xxx")// 表明在xxx秒内,不需要再发送预检验请求,可以缓存该结果c.Header("Access-Control-Max-Age", "3600")//如果method是OPTIONS,直接返回成功//有的模板是要请求两次的if method == "OPTIONS" {ctx.AbortWithStatusJSON(http.StatusOK, "Options Request!")retrun}// 处理请求c.Next()}}
