如何在GoFiber+GORM中移除响应里的冗余User子元素
解决GoFiber+GORM分页返回中Post对象冗余User子元素的问题
问题描述
使用GoFiber框架搭配GORM和github.com/morkid/paginate分页组件实现用户列表接口时,预加载用户的Post关联数据后,响应结果中的每个Post对象都包含了冗余的空User子元素,需要移除该子元素,同时保留UserID字段。
当前响应示例
{"data": {"items": [{"ID": 1,"CreatedAt": "2023-01-22T02:33:12.604+08:00","UpdatedAt": "2023-01-22T02:33:12.604+08:00","DeletedAt": null,"email": "ilham@mail.com","username": "","names": "","posts": [{"id": 1,"created_at": "2023-01-22T01:59:42.611+08:00","updated_at": "2023-01-22T01:59:42.611+08:00","deleted_at": null,"title": "using loop","body": "tes body postman 2","UserID": 1,"User": {"ID": 0,"CreatedAt": "0001-01-01T00:00:00Z","UpdatedAt": "0001-01-01T00:00:00Z","DeletedAt": null,"email": "","username": "","names": "","posts": null}}]}],"page": 0,"size": 10,"max_page": 1,"total_pages": 1,"total": 1,"last": false,"first": true,"visible": 1}}
期望响应示例
{"data": {"items": [{"ID": 1,"CreatedAt": "2023-01-22T02:33:12.604+08:00","UpdatedAt": "2023-01-22T02:33:12.604+08:00","DeletedAt": null,"email": "ilham@mail.com","username": "","names": "","posts": [{"id": 1,"created_at": "2023-01-22T01:59:42.611+08:00","updated_at": "2023-01-22T01:59:42.611+08:00","deleted_at": null,"title": "using loop","body": "tes body postman 2","UserID": 1}]}],"page": 0,"size": 10,"max_page": 1,"total_pages": 1,"total": 1,"last": false,"first": true,"visible": 1}}
现有代码
User模型
package models import "gorm.io/gorm" type User struct { gorm.Model Email string `gorm:"unique" json:"email"` Username string `gorm:"unique" json:"username"` Password string `json:"-"` Names string `json:"names"` Post []Post `gorm:"Foreignkey:UserID;association_foreignkey:ID;" json:"posts"` }
Post模型
package models import ( "time" "gorm.io/gorm" ) type Post struct { ID uint `gorm:"primaryKey" json:"id"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at"` Title string `json:"title"` Body string `json:"body"` UserID uint `gorm:"column:user_id"` User User }
处理器代码
func GetAllUser(c *fiber.Ctx) error { var users []models.User model := initializers.DB.Preload("Post").Find(&users) return c.Status(fiber.StatusOK).JSON(fiber.Map{ "data": initializers.PG.With(model).Request(c.Request()).Response(&[]models.User{}), }) }
解决方案
方法1:修改Post模型的JSON标签(推荐)
直接在Post结构体的User字段上添加json:"-"标签,这样JSON序列化时会自动忽略该字段,不会返回给前端。修改后的Post模型如下:
package models import ( "time" "gorm.io/gorm" ) type Post struct { ID uint `gorm:"primaryKey" json:"id"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at"` Title string `json:"title"` Body string `json:"body"` UserID uint `gorm:"column:user_id" json:"user_id"` // 可选:添加json标签统一字段命名风格 User User `json:"-"` // 关键:添加该标签让序列化忽略User字段 }
这种方法简单高效,不需要修改处理器逻辑,适合不需要在其他接口中返回Post关联User的场景。
方法2:使用响应专用结构体(灵活适配多场景)
如果需要保留模型的原有JSON序列化逻辑,或者不同接口需要返回不同的字段结构,可以定义专门用于响应的结构体,将查询到的模型数据映射到响应结构体中。
1. 定义响应结构体
package models import "gorm.io/gorm" // UserResponse 用于用户列表接口的响应结构体 type UserResponse struct { gorm.Model Email string `json:"email"` Username string `json:"username"` Names string `json:"names"` Posts []PostResponse `json:"posts"` } // PostResponse 用于用户列表接口中Post的响应结构体 type PostResponse struct { ID uint `json:"id"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` DeletedAt gorm.DeletedAt `json:"deleted_at"` Title string `json:"title"` Body string `json:"body"` UserID uint `json:"user_id"` }
2. 修改处理器代码
将查询到的User模型数据转换为UserResponse,再传入分页组件的Response方法:
func GetAllUser(c *fiber.Ctx) error { var users []models.User model := initializers.DB.Preload("Post").Find(&users) // 转换模型数据到响应结构体 var userResponses []models.UserResponse for _, u := range users { postResponses := make([]models.PostResponse, len(u.Post)) for i, p := range u.Post { postResponses[i] = models.PostResponse{ ID: p.ID, CreatedAt: p.CreatedAt, UpdatedAt: p.UpdatedAt, DeletedAt: p.DeletedAt, Title: p.Title, Body: p.Body, UserID: p.UserID, } } userResponses = append(userResponses, models.UserResponse{ Model: u.Model, Email: u.Email, Username: u.Username, Names: u.Names, Posts: postResponses, }) } // 传入响应结构体切片到分页组件 return c.Status(fiber.StatusOK).JSON(fiber.Map{ "data": initializers.PG.With(model).Request(c.Request()).Response(&userResponses), }) }
这种方法更灵活,适合需要为不同接口定制响应结构的场景,但需要额外编写数据映射逻辑。
内容的提问来源于stack exchange,提问作者ilham tenriajeng
相关产品推荐
相关产品推荐

