Web API中含接口属性的Model提交后未进入Controller返回400错误问题
问题引发原因
ASP.NET Core 默认的模型绑定器在反序列化客户端提交的JSON数据时,无法直接实例化接口或者抽象类类型的对象。你定义的CreateNewOrganizationModel里三个属性分别是IOrganizationDetailDto、IList<IUserRoleDetailDto>、IPersonalDetailDto接口类型,反序列化时系统不知道要创建哪个实现类的实例,所以抛出了报错里的"Type is an interface or abstract class and cannot be instantiated"异常,最终返回400 Bad Request错误。
解决方案
这里提供3种常用的可行方案,你可以根据自己的项目情况选择:
- 方案1:将Model里的接口类型替换为对应的实现类(最常用、最简单)
直接把属性声明为你写的对应接口实现类即可,比如你有OrganizationDetailDto实现了IOrganizationDetailDto,就修改Model如下:
public class CreateNewOrganizationModel : ICreateNewRegistrationDto { [Required(ErrorMessage = "The {0} is required.")] public OrganizationDetailDto OrganizationDetail { get; set; } [Required(ErrorMessage = "The {0} is required.")] public IList<UserRoleDetailDto> UserRoleDetails { get; set; } [Required(ErrorMessage = "The {0} is required.")] public PersonalDetailDto PersonalDetail { get; set; } }
- 方案2:自定义JSON反序列化转换器,指定接口对应的实现类
如果你必须保留属性的接口类型声明,可以给接口加JsonConverter特性,或者全局配置反序列化规则,告诉序列化器遇到该接口时用哪个实现类实例化,以System.Text.Json为例:
首先创建通用的接口转换器:
public class InterfaceConverter<TInterface, TImplementation> : JsonConverter<TInterface> where TImplementation : class, TInterface, new() { public override TInterface Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return JsonSerializer.Deserialize<TImplementation>(ref reader, options); } public override void Write(Utf8JsonWriter writer, TInterface value, JsonSerializerOptions options) { JsonSerializer.Serialize(writer, value, value.GetType(), options); } }
然后在Program.cs里注册转换器:
builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.Converters.Add(new InterfaceConverter<IOrganizationDetailDto, OrganizationDetailDto>()); options.JsonSerializerOptions.Converters.Add(new InterfaceConverter<IPersonalDetailDto, PersonalDetailDto>()); options.JsonSerializerOptions.Converters.Add(new InterfaceConverter<IUserRoleDetailDto, UserRoleDetailDto>()); });
- 方案3:使用Newtonsoft.Json替换默认的System.Text.Json,并配置类型名处理
如果项目中用Newtonsoft.Json,你可以配置序列化时携带类型名,反序列化时根据类型名自动实例化对应的实现类,不过这个方案需要客户端提交JSON时也携带$type字段,耦合度较高,不推荐对外公开的API使用。
内容的提问来源于stack exchange,提问作者mnu-nasir
相关产品推荐
相关产品推荐

