r.GET(“/benchmark”, before, main,after)
使用中间件
func main() {
// 创建一个默认的没有任何中间件的路由
r := gin.New()
// 全局中间件
// Logger 中间件将写日志到 gin.DefaultWriter ,即使你设置 GIN_MODE=release 。
// 默认 gin.DefaultWriter = os.Stdout
r.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 group
testing := authorized.Group("testing")
testing.GET("/analytics", analyticsEndpoint)
}
// 监听并服务于 0.0.0.0:8080
r.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:8080
r.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)
// 因为我们没有使用协成,我们不需要复制 context
log.Println("Done! in path " + c.Request.URL.Path)
})
// 监听并服务于 0.0.0.0:8080
r.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/secrets
authorized.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:8080
r.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 middle
import (
"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.Path
for _,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 middle
import (
"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.Method
c.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读取到Header
c.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()
}
}