package middleware import ( "bytes" "encoding/json" "net/http" "strings" "github.com/gin-gonic/gin" ) // responseWrapper 捕获 handler 输出的中间件 // 将所有裸 JSON 响应自动包装为 {code: 0, message: "success", data: ...} 格式 type responseWrapper struct { gin.ResponseWriter body *bytes.Buffer statusCode int } func (w *responseWrapper) Write(b []byte) (int, error) { w.body.Write(b) // 不再同时写到原始 writer,让 body 完全缓冲 return len(b), nil } func (w *responseWrapper) WriteString(s string) (int, error) { w.body.WriteString(s) return len(s), nil } func (w *responseWrapper) WriteHeader(code int) { w.statusCode = code // 不实际写入,让 gin 的最终写入处理 } // ResponseWrapper 返回包装响应格式的中间件 func ResponseWrapper() gin.HandlerFunc { return func(c *gin.Context) { // 跳过非 JSON 响应(如文件下载、流式响应) contentType := c.GetHeader("Content-Type") if strings.Contains(contentType, "text/event-stream") || contentType == "application/octet-stream" || strings.HasPrefix(c.Request.URL.Path, "/swagger/") { c.Next() return } // 包装 response writer 以捕获输出 wrapper := &responseWrapper{ ResponseWriter: c.Writer, body: bytes.NewBuffer(nil), statusCode: http.StatusOK, } c.Writer = wrapper c.Next() // 检查是否已标记为已包装 if _, exists := c.Get("response_wrapped"); exists { // 直接把捕获的内容写回到底层 writer wrapper.ResponseWriter.WriteHeader(wrapper.statusCode) wrapper.ResponseWriter.Write(wrapper.body.Bytes()) return } // 只处理成功响应(2xx) if wrapper.statusCode < 200 || wrapper.statusCode >= 300 { // 非成功状态,直接把捕获的内容写回 wrapper.ResponseWriter.WriteHeader(wrapper.statusCode) wrapper.ResponseWriter.Write(wrapper.body.Bytes()) return } // 解析捕获的 body if wrapper.body.Len() == 0 { wrapper.ResponseWriter.WriteHeader(wrapper.statusCode) return } bodyBytes := wrapper.body.Bytes() // 尝试解析为 JSON 对象 var raw json.RawMessage if err := json.Unmarshal(bodyBytes, &raw); err != nil { // 不是有效 JSON,不包装 wrapper.ResponseWriter.WriteHeader(wrapper.statusCode) wrapper.ResponseWriter.Write(bodyBytes) return } // 检查是否已经是标准格式(有 code 字段) var checkMap map[string]interface{} if err := json.Unmarshal(bodyBytes, &checkMap); err == nil { if _, hasCode := checkMap["code"]; hasCode { // 已经是标准格式,不重复包装 wrapper.ResponseWriter.WriteHeader(wrapper.statusCode) wrapper.ResponseWriter.Write(bodyBytes) return } } // 包装为标准格式 wrapped := map[string]interface{}{ "code": 0, "message": "success", "data": raw, } wrappedBytes, err := json.Marshal(wrapped) if err != nil { wrapper.ResponseWriter.WriteHeader(wrapper.statusCode) wrapper.ResponseWriter.Write(bodyBytes) return } // 设置响应头并写入包装后的内容 wrapper.ResponseWriter.Header().Set("Content-Type", "application/json") wrapper.ResponseWriter.WriteHeader(wrapper.statusCode) wrapper.ResponseWriter.Write(wrappedBytes) } } // WrapResponse 标记响应为已包装,防止重复包装 // handler 中使用 response.Success() 等方法后调用此函数 func WrapResponse(c *gin.Context) { c.Set("response_wrapped", true) } // NoWrapper 跳过包装的中间件处理器 func NoWrapper() gin.HandlerFunc { return func(c *gin.Context) { WrapResponse(c) c.Next() } }