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

如何在使用DTO处理PATCH部分更新时应对键缺失问题

Go中处理PATCH接口部分更新的几种实用方案

在处理PATCH接口的部分更新时,核心问题是区分「客户端没传该字段」和「客户端主动传了null」,避免把未传字段误更新为NULL。不用map检查字段存在性的话,有这几种靠谱的解决方法:

1. 用指针的指针区分状态

把需要区分的字段改成**string类型,这样三种状态就能明确区分:

  • 客户端没传字段:字段值为nil
  • 客户端传了null:字段值为(*string)(nil)
  • 客户端传了具体值:字段值为指向对应字符串的指针

修改后的结构体:

type UpdateUserDTO struct {
  ID         uuid.UUID
  FirstName  *string
  LastName   *string
  ImageURL   **string
}

处理逻辑示例:

// 假设从数据库查询出原始用户数据originalUser
if dto.ImageURL != nil {
  // 客户端传了这个字段,处理null或具体值
  if *dto.ImageURL == nil {
    originalUser.ImageURL = nil // 主动设为数据库NULL
  } else {
    originalUser.ImageURL = *dto.ImageURL // 更新为传入的具体值
  }
}
// 没传该字段则直接跳过,保留数据库原有值

这种方法无需额外定义类型,适合字段不多的简单场景,但嵌套指针的写法读起来稍显绕。

2. 自定义可选类型跟踪字段是否被设置

定义一个带标记的自定义类型,实现UnmarshalJSON接口,主动标记该字段是否被客户端传入。

先定义可选字符串类型:

type OptionalString struct {
  Value string
  Set   bool // 标记是否被客户端设置过
}

// 只要触发解码就标记Set为true,区分未传字段的默认状态
func (os *OptionalString) UnmarshalJSON(data []byte) error {
  os.Set = true
  // 如果传的是null,直接返回,Value保持默认空字符串
  if string(data) == "null" {
    return nil
  }
  // 解析具体值到Value
  return json.Unmarshal(data, &os.Value)
}

更新结构体:

type UpdateUserDTO struct {
  ID         uuid.UUID
  FirstName  OptionalString
  LastName   OptionalString
  ImageURL   OptionalString
}

处理逻辑清晰直观:

if dto.ImageURL.Set {
  // 客户端传了这个字段,判断是null还是具体值
  if string(data) == "null" {
    originalUser.ImageURL = nil
  } else {
    originalUser.ImageURL = &dto.ImageURL.Value
  }
}
// 未设置则跳过,保留数据库原有值

这种方法扩展性强,不管是string、int还是其他类型,都可以套用这个模板定义对应的Optional类型,代码可读性也更高。

3. 自定义SQL Null类型(贴近数据库交互场景)

如果你的数据库字段用的是sql.NullString这类类型,可以修改它的解码逻辑,让「未传字段」和「传null」区分开。默认的sql.NullString无法区分这两种状态,所以需要自定义扩展:

type CustomNullString struct {
  sql.NullString
  Set bool // 标记是否被客户端传入
}

func (c *CustomNullString) UnmarshalJSON(data []byte) error {
  c.Set = true
  if string(data) == "null" {
    c.Valid = false
    return nil
  }
  if err := json.Unmarshal(data, &c.String); err != nil {
    return err
  }
  c.Valid = true
  return nil
}

结构体里使用CustomNullString,处理时检查Set属性即可,逻辑和第二种方案类似。这种方案更贴近数据库类型,适合直接和数据库交互的场景。

内容的提问来源于stack exchange,提问作者lkp

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 19:17:33