C#中区分Null值与缺失值的现有类型查询(Partial Update API)
ASP.NET Core 部分更新API的三种状态区分方案
在开发Partial Update API时,要区分「有值」「null值」「未指定」三种状态,ASP.NET Core里有几种现成方案不用自己造轮子:
1. 用JsonNode手动解析(无依赖轻量方案)
直接用System.Text.Json.Nodes.JsonNode接收请求体,通过判断属性是否存在来区分状态:
[HttpPatch("/api/entities/{id}")] public IActionResult PatchEntity(int id, JsonNode patchData) { var patchObj = patchData.AsObject(); // 检查kind是否被指定(不管值是null还是具体内容) if (patchObj.ContainsKey("kind")) { string? kindValue = patchObj["kind"]?.GetValue<string>(); // 执行kind字段更新逻辑:kindValue为null时重置,有值时替换 } // 同理处理description if (patchObj.ContainsKey("description")) { string? descValue = patchObj["description"]?.GetValue<string>(); // 执行description字段更新逻辑 } return Ok(); }
这种方式不用额外定义类型,适合字段较少的场景,缺点是需要手动处理每个属性。
2. 使用第三方Optional<T>库(成熟方案)
可以用NuGet上的Optional包,它已经实现了能区分三种状态的Optional<T>类型:
- 先安装包:
Install-Package Optional - 定义Patch用的DTO:
public class EntityPatchDto { public Optional<string> Kind { get; set; } public Optional<string> Description { get; set; } }
- 在Action中使用:
[HttpPatch("/api/entities/{id}")] public IActionResult PatchEntity(int id, EntityPatchDto patchDto) { // HasValue为true表示该属性被指定(不管值是null还是具体内容) if (patchDto.Kind.HasValue) { string? kind = patchDto.Kind.ValueOr(null); // 更新kind字段 } if (patchDto.Description.HasValue) { string? description = patchDto.Description.ValueOr(null); // 更新description字段 } return Ok(); }
这个库会自动处理JSON序列化逻辑:未提交的属性会是Optional<T>.None,提交null则是Optional<T>.Some(null),提交具体值就是Optional<T>.Some(具体值),完美区分三种状态。
3. 自定义极简版Optional<T>(无第三方依赖)
如果不想引入外部包,可以自己写一个轻量的Optional<T>,配合自定义JSON转换器:
// 定义Optional<T>结构体 public struct Optional<T> { public bool HasValue { get; } public T Value { get; } private Optional(T value) { HasValue = true; Value = value; } public static Optional<T> Some(T value) => new(value); public static Optional<T> None => new(); } // 自定义JSON转换器 public class OptionalJsonConverter<T> : JsonConverter<Optional<T>> { public override Optional<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var value = JsonSerializer.Deserialize<T>(ref reader, options); return Optional<T>.Some(value); } public override void Write(Utf8JsonWriter writer, Optional<T> value, JsonSerializerOptions options) { if (value.HasValue) JsonSerializer.Serialize(writer, value.Value, options); else writer.WriteNullValue(); } }
然后在Program.cs里注册转换器:
builder.Services.AddControllers() .AddJsonOptions(options => { // 为需要的类型添加转换器,比如string、int等 options.JsonSerializerOptions.Converters.Add(new OptionalJsonConverter<string>()); options.JsonSerializerOptions.Converters.Add(new OptionalJsonConverter<int?>()); });
使用方式和第三方库一致,DTO中用Optional<T>作为属性类型即可。
内容的提问来源于stack exchange,提问作者Dirk Boer
相关产品推荐
相关产品推荐

