如果未找到公共默认构造函数,则ASP.NET Core使用非默认构造函数

耶尔达·库曼加利耶夫(Yeldar Kurmangaliyev)

我正在编写在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");
    }
}

我通过null用Fiddler调用值来测试它

PUT /MyController (Content-Type: application/json)
{
    "MyProperty": null
}

我遇到的问题是,我的公共构造函数在myProperty等于的情况下被调用null,这导致ArgumentNullException抛出异常并导致500 Internal Server Error。

我期望的是它将使用私有的无参数构造函数和私有的setter方法。然后,由于控制器已标记了ApiController属性,因此将根据数据注释自动验证该模型,并将导致出现400 Bad Request(由于MyProperty需要)。

有趣的是-如果我将默认的构造函数公开,那么它可以按预期工作,但是我不想这样做。

为什么不使用私有构造函数?如何在不将其标记为公共的情况下使用它?

另一个问题是模型联编程序是否了解如何通过反射使用带参数的构造函数?

耶尔达·库曼加利耶夫(Yeldar Kurmangaliyev)

感谢Panagiotis Kanavos指出在ASP.NET Core中使用了Json.NET序列化程序。
这导致我ConstructorHandling在Json.NET文档中进行设置

行为原因

该文档指定以下内容:

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(服务)

另一种方法是更改要使用ConstructionHandlingJsonSerializerSettings属性AllowNonPublicDefaultConstructor

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

这是在以下位置完成的方法Startup.cs

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

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

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

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

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

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

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章