package middleware import ( "fmt" "runtime/debug" "strings" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" "easygoadmin/internal/consts" "easygoadmin/utility/response" ) // ============================== 统一响应兜底中间件 ============================== // Response 统一响应兜底 // // 负责 panic 恢复并输出统一 JSON 错误(前端契约 {code,msg,data}), // 置于跨域之后、认证之前,保证任何环节的 panic 都不会泄漏堆栈到前端。 // 同时将请求元信息(IP、UA、方法、URL)写入上下文,供登录日志与操作日志采集。 func Response(r *ghttp.Request) { // SVG 静态文件加固:强制附件下载 + nosniff(防内嵌脚本同源执行) if strings.HasSuffix(strings.ToLower(r.URL.Path), ".svg") { r.Response.Header().Set("Content-Disposition", "attachment") r.Response.Header().Set("X-Content-Type-Options", "nosniff") } // 请求元信息(所有路由均需,登录日志依赖) r.SetCtx(consts.WithRequestMeta(r.Context(), realClientIP(r), r.Header.Get("User-Agent"), r.Method, r.URL.String(), )) defer func() { if e := recover(); e != nil { g.Log().Errorf(r.Context(), "panic recovered: %v\n%s", e, debug.Stack()) if _, written := response.GetCode(r); written { return // 已写出响应,仅记录 } response.WriteWithCode(r, response.Fail(gerror.NewCode(consts.CodeServerError, fmt.Sprintf("服务器内部错误: %v", e))), 500) } }() r.Middleware.Next() }