繁体   English   中英

如果未找到公共默认构造函数,则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按以下顺序搜索构造函数:

  • 公共默认构造函数
  • 公共参数化构造函数
  • 私有默认构造函数

这就是为什么使用参数化构造函数优于私有默认构造函数的原因。

使用私有默认ctor代替公共参数化ctor(一类)

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反序列化器将使用显式指定的构造函数。

使用私有默认ctor代替公共参数化ctor(服务)

另一种方法是将JsonSerializerSettings属性的ConstructionHandling更改为使用AllowNonPublicDefaultConstructor

ConstructionHandling.AllowNonPublicDefaultConstructor: Json.NET将使用非公共默认构造函数,然后再返回到参数化构造函数。

这是可以在Startup.cs完成的方法:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().AddJsonOptions(o => {
        o.SerializerSettings.ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor;
    });
}

这会将这种逻辑应用于所有模型,并且对于所有模型,反序列化器将始终首选私有默认构造函数而不是公共参数化构造函数。

请求模型中的参数化构造函数可能是代码异味

在此特定示例中,已提供了代码以重现该问题。

在实际代码中,参数化或多个构造函数可能意味着您将类用于多种用途,即域模型和请求模型。 最终可能导致重用或支持此代码的问题。

具有公共默认构造函数且没有逻辑的DTO应当用于请求以避免这些问题。

您想要的是不合逻辑的。 private成员(包括构造函数)无法从外部访问

如果一个类具有一个或多个私有构造函数而没有公共构造函数,则其他类(嵌套类除外)无法创建该类的实例。

要绑定模型 ,控制器只有一种方法,即将每个参数设置为null公共c-tor调用。

为了使模型绑定成为可能,该类必须具有公共默认构造函数和公共可写属性进行绑定。 发生模型绑定时,可以使用公共默认构造函数实例化该类,然后可以设置属性。

因此,要成功绑定,您应该:

  1. 具有公共默认值控制
  2. 有财产的公共二传手。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM