Go-web(二)异步请求
异步请求
goroutine机制可以方便地实现异步处理,另外,在启动新的goroutine时,不应该使用原始上下文,必须使用它的只读副本。
// 异步处理请求
r.GET("/async", func(context *gin.Context) {
// copy请求
copyContext := context.Copy()
go func() {
time.Sleep(3 * time.Second)
fmt.Println("异步执行:",copyContext.Request.URL.Path)
}()
context.JSON(200,"执行成功")
})
r.Run(":8080")
请求后,会立马返回执行成功,异步操作执行完成后,会返回请求url路径。
重定向
r.GET("/redict", func(context *gin.Context) {
context.Redirect(302,"/async")
})
中间件
gin中的中间件是一个http请求的拦截器。
定义中间件:
// 编写一个返回值是HandlerFunc的方法即可
func Mid1() gin.HandlerFunc {
return func(context *gin.Context) {
fmt.Println("mid1 start")
//context.Next()
fmt.Println("mid1 end")
}
}
HandlerFunc是gin中自定义的类型:
type HandlerFunc func(*Context)
注册中间件:
// 注册全局中间件
r.Use(Mid1(),Mid2(),Mid3())
看一个demo:
注册三个中间件,都不调用Next方法:
// 注册全局中间件
r.Use(Mid1(),Mid2(),Mid3())
r.GET("/useMid", func(context *gin.Context) {
fmt.Println(".... request start ....")
time.Sleep(3 * time.Second)
fmt.Println(".... request end ....")
context.JSON(200,"")
})
func Mid1() gin.HandlerFunc {
return func(context *gin.Context) {
fmt.Println("mid1 start")
//context.Next()
fmt.Println("mid1 end")
}
}
func Mid2() gin.HandlerFunc {
return func(context *gin.Context) {
fmt.Println("mid2 start")
//context.Next()
fmt.Println("mid2 end")
}
}
func Mid3() gin.HandlerFunc {
return func(context *gin.Context) {
fmt.Println("mid3 start")
//context.Next()
fmt.Println("mid3 end")
}
}
执行结果:
不调用Next方法,中间件会按照注册的顺序执行,最后执行请求。可以用来做一些前置处理,如鉴权限流啥的
在看一个调用Next方法的:
// 注册全局中间件
r.Use(Mid1(),Mid2(),Mid3())
r.GET("/useMid", func(context *gin.Context) {
fmt.Println(".... request start ....")
time.Sleep(3 * time.Second)
fmt.Println(".... request end ....")
context.JSON(200,"")
})
func Mid1() gin.HandlerFunc {
return func(context *gin.Context) {
fmt.Println("mid1 start")
context.Next()
fmt.Println("mid1 end")
}
}
func Mid2() gin.HandlerFunc {
return func(context *gin.Context) {
fmt.Println("mid2 start")
context.Next()
fmt.Println("mid2 end")
}
}
func Mid3() gin.HandlerFunc {
return func(context *gin.Context) {
fmt.Println("mid3 start")
context.Next()
fmt.Println("mid3 end")
}
}
执行结果:
程序会在调用Next方法地方执行下一个中间件,并且执行顺序是按照中间件的注册顺序。实际执行的请求可以理解为一个默认的中间件。
如果一个中间件调用了Abort方法:
// 注册全局中间件
r.Use(Mid1(),Mid2(),Mid3())
r.GET("/useMid", func(context *gin.Context) {
fmt.Println(".... request start ....")
time.Sleep(3 * time.Second)
fmt.Println(".... request end ....")
context.JSON(200,"")
})
func Mid1() gin.HandlerFunc {
return func(context *gin.Context) {
fmt.Println("mid1 start")
context.Next()
fmt.Println("mid1 end")
}
}
func Mid2() gin.HandlerFunc {
return func(context *gin.Context) {
fmt.Println("mid2 start")
context.Abort()
fmt.Println("mid2 end")
}
}
func Mid3() gin.HandlerFunc {
return func(context *gin.Context) {
fmt.Println("mid3 start")
context.Next()
fmt.Println("mid3 end")
}
}
结果:
后续的请求和中间件都不会执行。
所以:如果某个中间件调用了c.Abort(),则此中间件结束后会直接返回,后面的中间件均不会调用。
gin默认注册了日志中间件Logger()和异常捕获中间件Recovery()
Logger()
func Logger() HandlerFunc {
return LoggerWithConfig(LoggerConfig{})
}
Recovery()
func Recovery() HandlerFunc {
return RecoveryWithWriter(DefaultErrorWriter)
}
底层就是通过recover()方法捕获panic做处理:
func CustomRecoveryWithWriter(out io.Writer, handle RecoveryFunc) HandlerFunc {
var logger *log.Logger
if out != nil {
logger = log.New(out, "\n\n\x1b[31m", log.LstdFlags)
}
return func(c *Context) {
defer func() {
if err := recover(); err != nil {
// Check for a broken connection, as it is not really a
// condition that warrants a panic stack trace.
var brokenPipe bool
if ne, ok := err.(*net.OpError); ok {
if se, ok := ne.Err.(*os.SyscallError); ok {
if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
brokenPipe = true
}
}
}