如果未找到公共默认构造函数,则ASP.NET Core将使用非默认构造函数 [英] ASP.NET Core is using non-default constructor if no public default constructor found

查看:155
本文介绍了如果未找到公共默认构造函数,则ASP.NET Core将使用非默认构造函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写在Service Fabric上托管的ASP.NET Core 2.2.0应用程序.

I am writing ASP.NET Core 2.2.0 application hosted on Service Fabric.

我有一个代表请求的类,并且我声明了两个构造函数:public(供我自己使用)和private(供序列化器使用):

I have a class which represents a request and I have declared two constructors: public for my own usage and private for serializers:

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控制器:

Then, I created an API controller:

[ApiController]
public class MyController
{
    [HttpPut]
    public async Task<IActionResult> Save([FromBody] MyClass model)
    {
        throw new NotImplementedException("Doesn't matter in this example");
    }
}

然后我用Fiddler调用null值对其进行测试:

And I test it by calling with null value with Fiddler:

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

问题,我遇到的问题是调用myProperty等于null的公共构造函数,这导致抛出ArgumentNullException并导致500 Internal Server Error.

The problem I encounter is that my public constructor is called with myProperty equal to null, which causes ArgumentNullException to be thrown and results into 500 Internal Server Error.

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

What I expected is that it will use private parameterless constructor and private setters. Then, since the controller is marked with ApiController attribute, this model will be validated automatically against data annotations and will result into 400 Bad Request, because MyProperty is required.

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

What is interesting - if I make the default constructor public, then it works as expected, but I wouldn't want to do this.

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

Why doesn't it use private constructor and how can I make it use it without marking it as public?

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

Another question is does model binder understand how to use constructor with parameters using reflection?

推荐答案

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

Thanks to Panagiotis Kanavos for pointing out that Json.NET serializer is used in ASP.NET Core.
This led me to ConstructorHandling setting in the Json.NET documentation.

文档中指定了以下内容:

The documentation specifies the following:

ConstructionHandling.Default.首先尝试使用公共默认构造函数,然后回退到单个参数化的构造函数,然后再使用非公共默认构造函数.

ConstructionHandling.Default. First attempt to use the public default constructor, then fall back to a single parameterized constructor, then to the non-public default constructor.

也就是说,Json.NET按照以下顺序搜索构造函数:

That is Json.NET searches for the constructors in the following order:

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

这就是为什么使用参数化构造函数而不是私有默认构造函数的原因.

And this is the reason why parameterized constructor is preferred over private default constructor.

JsonConstructorAttribute可用于为Json.NET反序列化器显式指定构造函数:

JsonConstructorAttribute can be used to explicitly specify constructor to Json.NET deserializer:

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

Now Json.NET deserializer will use the explicitly specified constructor.

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

Another way is to change ConstructionHandling of JsonSerializerSettings property to use AllowNonPublicDefaultConstructor:

ConstructionHandling.AllowNonPublicDefaultConstructor: Json.NET在使用参数化构造函数之前将使用非公共默认构造函数.

ConstructionHandling.AllowNonPublicDefaultConstructor: Json.NET will use a non-public default constructor before falling back to a parameterized constructor.

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

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

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

This will apply this logic to all models and the deserializer will always prefer private default constructor over public parameterized constructors for all models.

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

In this particular example, the code has been presented to reproduce the problem.

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

In real code, parameterized or multiple constructors can imply that you are using classes for multiple purposes, i.e. domain model and request model. This can eventually lead to problems with reusing or supporting this code.

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

DTOs with public default constructor and no logic should be used for requests to avoid these problems.

这篇关于如果未找到公共默认构造函数,则ASP.NET Core将使用非默认构造函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆