package middleware import ( "net" "net/http" "net/http/httputil" "os" "runtime/debug" "strings" "kra/pkg/response" "github.com/gin-gonic/gin" "go.uber.org/zap" ) // 全局日志实例 var recoveryLogger *zap.Logger // SetRecoveryLogger 设置Recovery日志 func SetRecoveryLogger(logger *zap.Logger) { recoveryLogger = logger } // GinRecovery 自定义Recovery中间件(与 kra 保持一致) func GinRecovery(stack bool) gin.HandlerFunc { return func(c *gin.Context) { defer func() { if err := recover(); err != nil { // 检查是否为断开的连接 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 } } } httpRequest, _ := httputil.DumpRequest(c.Request, false) if brokenPipe { if recoveryLogger != nil { recoveryLogger.Error(c.Request.URL.Path, zap.Any("error", err), zap.String("request", string(httpRequest)), ) } // 如果连接已断开,无法写入状态 c.Error(err.(error)) c.Abort() return } if stack { if recoveryLogger != nil { recoveryLogger.Error("[Recovery from panic]", zap.Any("error", err), zap.String("request", string(httpRequest)), zap.String("stack", string(debug.Stack())), ) } } else { if recoveryLogger != nil { recoveryLogger.Error("[Recovery from panic]", zap.Any("error", err), zap.String("request", string(httpRequest)), ) } } response.FailWithMessage("服务器内部错误", c) c.Abort() } }() c.Next() } } // Cors 跨域中间件 func Cors() gin.HandlerFunc { return func(c *gin.Context) { method := c.Request.Method origin := c.Request.Header.Get("Origin") c.Header("Access-Control-Allow-Origin", origin) c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token,Authorization,Token,X-Token,X-User-Id") c.Header("Access-Control-Allow-Methods", "POST,GET,OPTIONS,DELETE,PUT") c.Header("Access-Control-Expose-Headers", "Content-Length,Access-Control-Allow-Origin,Access-Control-Allow-Headers,Content-Type,New-Token,New-Expires-At") c.Header("Access-Control-Allow-Credentials", "true") // 放行所有OPTIONS方法 if method == "OPTIONS" { c.AbortWithStatus(http.StatusNoContent) } c.Next() } }