ASP.NET接口如何同时支持接收整数或对象类型的关联字段参数
实现方案
最低侵入性的方案是为System.Text.Json实现针对这类关联实体集合的泛型自定义JsonConverter,仅对标记了对应特性的属性生效,完全不影响实体其他属性的默认处理逻辑,同时可以复用在所有同类需求的接口上。
步骤1:定义通用关联实体接口
首先定义一个仅包含Id字段的公共接口,所有需要兼容「id值/实体对象」两种传入格式的关联实体都实现该接口:
public interface IAssociatedEntity { int Id { get; set; } } // 你的Creator实体实现该接口 public class Creator : IAssociatedEntity { public int Id { get; set; } public string? Name { get; set; } // 其他属性保持不变 }
步骤2:实现泛型JsonConverter
写一个可复用的集合类型转换器,自动识别数组中的整数和对象格式,统一转换为对应的关联实体实例:
using System.Text.Json; using System.Text.Json.Serialization; public class AssociatedEntityCollectionConverter<T> : JsonConverter<IEnumerable<T>> where T : IAssociatedEntity, new() { public override IEnumerable<T>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.Null) { return null; } if (reader.TokenType != JsonTokenType.StartArray) { throw new JsonException("Expected array for associated entity collection"); } var result = new List<T>(); while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) { T entity = new T(); switch (reader.TokenType) { // 处理纯id的情况 case JsonTokenType.Number: entity.Id = reader.GetInt32(); break; // 处理完整对象的情况 case JsonTokenType.StartObject: // 用默认逻辑反序列化对象 entity = JsonSerializer.Deserialize<T>(ref reader, options)!; break; default: throw new JsonException("Unsupported element type in associated entity collection"); } result.Add(entity); } return result; } // 序列化保持默认行为,不需要修改 public override void Write(Utf8JsonWriter writer, IEnumerable<T> value, JsonSerializerOptions options) { JsonSerializer.Serialize(writer, value, options); } }
步骤3:绑定到对应属性
只需要在你需要兼容两种格式的集合属性上添加[JsonConverter]特性即可,其他属性完全不受影响:
public class WidgetUpdateDto { public string Name { get; set; } // 仅该属性会走自定义转换逻辑 [JsonConverter(typeof(AssociatedEntityCollectionConverter<Creator>))] public List<Creator> Creators { get; set; } }
如果多个接口都用到了相同类型的关联实体集合,也可以全局注册转换器,避免重复加特性:
// Program.cs中注册 builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.Converters.Add(new AssociatedEntityCollectionConverter<Creator>()); // 其他需要支持的关联实体类型可继续添加 });
方案优势
- 侵入性极低:仅修改需要特殊处理的属性标记,不会覆盖全局默认序列化逻辑,其他属性的处理完全不变
- 类型安全:不需要将属性声明为
object,编译期即可完成类型校验 - 通用可复用:所有同类多对多关联属性都可以通过实现
IAssociatedEntity接口复用这套转换逻辑 - 符合需求:既兼容用户直接传id数组的场景,也兼容GET获取数据修改后直接PUT回传的场景,后台始终可以直接取实体的Id值使用。
内容的提问来源于stack exchange,提问作者Mad Scientist
相关产品推荐
相关产品推荐

