.NET Core 2 API:如何结合依赖注入使用Options并处理接口属性?
解决.NET Core 2 API中接口类型属性序列化问题(结合DI与Options)
嘿,我来帮你搞定这个接口类型属性无法正常序列化/反序列化的问题!核心原因很简单:默认的JSON/XML序列化器不知道该用哪个具体类来实例化接口(毕竟接口不能直接new)。结合依赖注入和Options来解决的话,我们可以把接口-实现的映射集中配置,同时让序列化器能获取到这些配置。
步骤1:创建接口的具体实现类
首先,给你的ITestInterface写一个具体实现:
public class TestInterfaceImpl : ITestInterface { public string Hello { get; set; } }
步骤2:创建序列化配置类(Options)
我们用一个Options类来统一管理序列化相关的配置,比如接口到实现的映射、XML序列化需要的已知类型:
public class SerializationOptions { // 存储接口与对应实现类的映射 public Dictionary<Type, Type> InterfaceToImplementationMappings { get; set; } = new(); // XML序列化需要的已知类型集合 public List<Type> KnownXmlTypes { get; set; } = new(); }
步骤3:注册并配置Options
在ConfigureServices里注册这个Options,并添加你的接口-实现映射:
services.Configure<SerializationOptions>(options => { // 映射ITestInterface到TestInterfaceImpl options.InterfaceToImplementationMappings.Add(typeof(ITestInterface), typeof(TestInterfaceImpl)); // 添加XML序列化需要的已知类型 options.KnownXmlTypes.Add(typeof(TestInterfaceImpl)); });
步骤4:自定义JSON序列化的ContractResolver
针对你代码里用的Newtonsoft.Json,我们写一个依赖Options的自定义ContractResolver,让它能根据配置自动为接口创建实例:
public class DIEnabledContractResolver : DefaultContractResolver { private readonly IOptions<SerializationOptions> _serializationOptions; public DIEnabledContractResolver(IOptions<SerializationOptions> serializationOptions) { _serializationOptions = serializationOptions; } protected override JsonContract CreateContract(Type objectType) { var contract = base.CreateContract(objectType); // 如果当前类型是接口,且有对应的实现类配置,就指定默认创建器 if (objectType.IsInterface && _serializationOptions.Value.InterfaceToImplementationMappings.TryGetValue(objectType, out var implementationType)) { contract.DefaultCreator = () => Activator.CreateInstance(implementationType); } return contract; } }
然后把这个Resolver注册到DI容器:
services.AddSingleton<DIEnabledContractResolver>();
步骤5:配置Mvc的序列化选项
最后,在AddMvc里替换JSON的ContractResolver,并配置XML序列化的已知类型:
// 先获取序列化配置 var serializationOptions = services.BuildServiceProvider().GetRequiredService<IOptions<SerializationOptions>>().Value; services.AddMvc(options => options.RespectBrowserAcceptHeader = true) .AddXmlDataContractSerializerFormatters() .AddXmlSerializerFormatters(o => { // 给XML序列化器添加已知类型 foreach (var type in serializationOptions.KnownXmlTypes) { o.XmlSerializerOptions.KnownTypes.Add(type); } }) .AddJsonOptions(o => { o.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented; // 使用我们自定义的ContractResolver o.SerializerSettings.ContractResolver = services.BuildServiceProvider().GetRequiredService<DIEnabledContractResolver>(); });
为什么这样做?
- 用Options集中管理序列化配置,后续新增接口-实现映射只需要修改Options的配置,不用改序列化器的代码,符合开闭原则。
- 依赖注入的方式让我们的自定义Resolver能轻松获取配置,避免硬编码。
- 相比
TypeNameHandling这种方式,自定义ContractResolver更安全,不会引入反序列化漏洞。
内容的提问来源于stack exchange,提问作者Fred Johnson
相关产品推荐
相关产品推荐

