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

.NET 5 RESTful API如何仅更新JSON请求中传递的字段?

实现.NET 5 RESTful API的部分字段更新

你的问题在EF Core开发里很常见——直接把接收的Pillar实体标记为EntityState.Modified时,EF会认为所有字段都需要更新,而未在JSON中传递的字段会被初始化为默认值(比如null、0、false),最终覆盖数据库里的原有数据。要实现仅更新传递的字段,这里有几种靠谱的解决方案:

方案1:使用HttpPatch + JsonPatchDocument(推荐)

RESTful规范中,PATCH方法就是专门用于部分资源更新的。结合JsonPatchDocument,可以精准控制要更新的字段。

步骤1:修改控制器方法

[HttpPatch("{id}")]
public async Task<IActionResult> PatchPillar(int id, JsonPatchDocument<Pillar> patchDoc)
{
    if (patchDoc == null)
    {
        return BadRequest("补丁文档不能为空");
    }

    // 从数据库获取原始实体
    var existingPillar = await _context.Pillars.FindAsync(id);
    if (existingPillar == null)
    {
        return NotFound($"ID为{id}的Pillar不存在");
    }

    // 将补丁应用到原始实体上
    patchDoc.ApplyTo(existingPillar, ModelState);

    // 验证模型有效性
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    await _context.SaveChangesAsync();
    return NoContent();
}

步骤2:在Postman中发送PATCH请求

请求体需要遵循JSON Patch规范,比如你要更新Name字段,请求体应该是:

[
    {
        "op": "replace",
        "path": "/name",
        "value": "Pillar 1"
    }
]

这种方式下,EF Core只会生成更新被修改字段的SQL,完全符合你的需求。

方案2:使用Put请求 + DTO手动处理

如果你坚持要用PUT方法,建议创建一个更新专用的DTO,用可空类型区分“未传递字段”和“传递了默认值”,然后手动复制需要更新的字段。

步骤1:创建更新DTO

public class UpdatePillarDto
{
    public int ID { get; set; }
    public string Name { get; set; }
    public bool? IsActive { get; set; } // 用可空类型表示该字段可选
    public int? IDEvaluationType { get; set; }
}

步骤2:修改控制器方法

[HttpPut("{id}")]
public async Task<IActionResult> PutPillar(int id, [FromBody] UpdatePillarDto updatedDto)
{
    if (id != updatedDto.ID)
    {
        return BadRequest("路径ID与请求体ID不匹配");
    }

    var existingPillar = await _context.Pillars.FindAsync(id);
    if (existingPillar == null)
    {
        return NotFound($"ID为{id}的Pillar不存在");
    }

    // 仅更新DTO中传递了有效值的字段
    if (!string.IsNullOrEmpty(updatedDto.Name))
    {
        existingPillar.Name = updatedDto.Name;
    }
    if (updatedDto.IsActive.HasValue)
    {
        existingPillar.IsActive = updatedDto.IsActive.Value;
    }
    if (updatedDto.IDEvaluationType.HasValue)
    {
        existingPillar.IDEvaluationType = updatedDto.IDEvaluationType.Value;
    }

    await _context.SaveChangesAsync();
    return NoContent();
}

这种方式的好处是能明确控制哪些字段可以被更新,避免意外修改关联数据(比如你的Competences集合)。

方案3:使用AutoMapper自动映射(进阶)

如果字段较多,手动赋值太繁琐,可以用AutoMapper的条件映射功能,自动忽略未传递的字段。

步骤1:配置AutoMapper映射规则

public class PillarProfile : Profile
{
    public PillarProfile()
    {
        CreateMap<UpdatePillarDto, Pillar>()
            // 仅当Name不为空时才更新
            .ForMember(dest => dest.Name, opt => opt.Condition(src => !string.IsNullOrEmpty(src.Name)))
            // 仅当IsActive有值时才更新
            .ForMember(dest => dest.IsActive, opt => opt.Condition(src => src.IsActive.HasValue))
            // 仅当IDEvaluationType有值时才更新
            .ForMember(dest => dest.IDEvaluationType, opt => opt.Condition(src => src.IDEvaluationType.HasValue))
            // 忽略不需要更新的关联集合
            .ForMember(dest => dest.Competences, opt => opt.Ignore());
    }
}

步骤2:在控制器中使用AutoMapper

private readonly IMapper _mapper;

// 构造函数注入IMapper
public PillarsController(YourDbContext context, IMapper mapper)
{
    _context = context;
    _mapper = mapper;
}

[HttpPut("{id}")]
public async Task<IActionResult> PutPillar(int id, [FromBody] UpdatePillarDto updatedDto)
{
    if (id != updatedDto.ID)
    {
        return BadRequest("路径ID与请求体ID不匹配");
    }

    var existingPillar = await _context.Pillars.FindAsync(id);
    if (existingPillar == null)
    {
        return NotFound($"ID为{id}的Pillar不存在");
    }

    // AutoMapper自动映射符合条件的字段
    _mapper.Map(updatedDto, existingPillar);

    await _context.SaveChangesAsync();
    return NoContent();
}

总结

  • 优先选择方案1(HttpPatch + JsonPatchDocument),符合RESTful规范,实现简洁且安全。
  • 如果必须用PUT,推荐结合DTO+手动赋值或DTO+AutoMapper,避免默认值覆盖数据库原有数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 09:47:40