[英]How can I call Web API non-default constructor in new ASP.NET Core
[英]ASP.NET Core is using non-default constructor if no public default constructor found
我正在编写在Service Fabric上托管的ASP.NET Core 2.2.0应用程序。
我有一个代表请求的类,并且已经声明了两个构造函数:public供我自己使用,private供序列化器使用:
public class MyClass
{
private MyClass() // for serializer
{
}
public MyClass(string myProperty) // for myself
{
MyProperty = myProperty ?? throw new ArgumentNullException(nameof(myProperty));
}
[Required]
public string MyProperty { get; private set; }
}
然后,我创建了一个API控制器:
[ApiController]
public class MyController
{
[HttpPut]
public async Task<IActionResult> Save([FromBody] MyClass model)
{
throw new NotImplementedException("Doesn't matter in this example");
}
}
我通过用Fiddler调用null
值来测试它:
PUT /MyController (Content-Type: application/json)
{
"MyProperty": null
}
我遇到的问题是,使用myProperty等于null
调用了我的公共构造函数,这导致引发ArgumentNullException
并导致500 Internal Server Error。
我期望的是它将使用私有的无参数构造函数和私有的setter方法。 然后,由于控制器已用ApiController
属性标记,此模型将根据数据注释自动进行验证,并且由于需要MyProperty
,因此将导致400 Bad Request。
有趣的是-如果我将默认的构造函数设为public,那么它可以按预期工作,但是我不想这样做。
为什么不使用私有构造函数?如何在不将其标记为公共的情况下使用它?
另一个问题是模型联编程序是否了解如何通过反射使用带参数的构造函数?
感谢Panagiotis Kanavos指出在ASP.NET Core中使用了Json.NET序列化程序。
这使我进入了Json.NET文档中的ConstructorHandling
设置 。
该文档指定以下内容:
ConstructionHandling.Default。 首先尝试使用公共默认构造函数,然后回退到单个参数化的构造函数,然后再使用非公共默认构造函数。
也就是说,Json.NET按以下顺序搜索构造函数:
这就是为什么使用参数化构造函数优于私有默认构造函数的原因。
JsonConstructorAttribute
可用于为Json.NET反序列化器显式指定构造函数:
using Newtonsoft.Json;
public class MyClass
{
[JsonConstructor]
private MyClass() // for serializer
{
}
public MyClass(string myProperty) // for myself
{
MyProperty = myProperty ?? throw new ArgumentNullException(nameof(myProperty));
}
[Required]
public string MyProperty { get; private set; }
}
现在,Json.NET反序列化器将使用显式指定的构造函数。
另一种方法是将JsonSerializerSettings
属性的ConstructionHandling
更改为使用AllowNonPublicDefaultConstructor
:
ConstructionHandling.AllowNonPublicDefaultConstructor: Json.NET将使用非公共默认构造函数,然后再返回到参数化构造函数。
这是可以在Startup.cs
完成的方法:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddJsonOptions(o => {
o.SerializerSettings.ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor;
});
}
这会将这种逻辑应用于所有模型,并且对于所有模型,反序列化器将始终首选私有默认构造函数而不是公共参数化构造函数。
在此特定示例中,已提供了代码以重现该问题。
在实际代码中,参数化或多个构造函数可能意味着您将类用于多种用途,即域模型和请求模型。 最终可能导致重用或支持此代码的问题。
具有公共默认构造函数且没有逻辑的DTO应当用于请求以避免这些问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.