You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 00:56:07