【问题标题】:Null Models with default arguments need to be instantiated as such具有默认参数的空模型需要这样实例化
【发布时间】:2021-10-01 05:25:33
【问题描述】:

我有以下使用 .NET 4.6 的 asp.net WebApi2 路由,说明了我遇到的问题:

[Route("books/{id}")]
[HttpGet]
public JsonResponse GetBooks(string id, [FromUri]DescriptorModel model)

使用以下模型:

public class DescriptorModel
{
    public bool Fiction { get; set; } = false;

    // other properties with default arguments here
}

我正在尝试允许将 Fiction 属性设置为默认值(如果在获取请求期间未指定)。

当我明确指定 Fiction 属性时,它可以正常工作:

curl -X GET --header 'Accept: application/json' 'http://127.0.0.1:11000/api/v1/books/516.375/?Fiction=false'

但是,在进行以下测试时(省略带有默认参数的属性):

curl -X GET --header 'Accept: application/json' 'http://127.0.0.1:11000/api/v1/books/516.375'

“模型”的值绑定为 null,这不是我想要的。我的问题是如何简单地允许在模型绑定过程期间/之后但在调用控制器的“GetBooks”操作方法之前实例化使用默认值定义的模型。

注意。我使用带有 GET 请求的模型的原因是,在 swagger 中记录要容易得多,因为我的 GET/POST 操作在许多情况下可以通过继承重用相同的模型。

【问题讨论】:

  • 只是尝试使用 [FromQuery] 而不是 [FromUri] 没有别的。它会工作
  • @masoud 不是来自 asp.net 核心的 [FromQuery]?我正在使用 asp.net。

标签: c# asp.net asp.net-web-api2 model-binding action-filter


【解决方案1】:

由于您将 id 用作 FromUri,因此您可以将模型与 get 一起使用的唯一方法是将 url 与查询字符串一起使用

[Route("~/GetBooks/{id?}")]
[HttpGet]
public IActionResult GetBooks(string id, [FromQuery] DescriptorModel model)

在这种情况下,您的网址应该是

'http://127.0.0.1:11000/api/v1/books/?Name=name&&fiction=true'

//or if fiction==false just
'http://127.0.0.1:11000/api/v1/books/?Name=name'

//or if want to use id
'http://127.0.0.1:11000/api/v1/books/123/?Name=name&&fiction=true'

按照您的方式使用模型仅适用于 [FromForm] 或 [FromBody]。 要将其用作 MVC 建议,请尝试此操作

[Route("books/{id}/{param1}/{param2}/{fiction?}")]
[HttpGet]
public JsonResponse GetBooks(string id, string param1, string param2, bool fiction)

顺便说一句,你不需要将 bool 设为默认值,因为它在任何情况下都默认为 false

如果您想使用 uri 中的 ID 和 DescriptorModel,只有在将 Id 添加到 DescriptorModel 时才能这样做

[Route("books/{id}/{param1}/{param2}/{fiction?}")]
[HttpGet]
public JsonResponse GetBooks(DescriptorModel model)

更新

如果您的 mvc 不支持 [FromQuery],您可以在这样的操作中使用 RequestQuery


 var value= context.Request.Query["value"];

但最好更新到 MVC 6。

【讨论】:

  • 您可以将模型用于带有 [FromUri] 属性的 Get 请求。这很方便,因此 get/posts 可以使用相同的模型,并且更容易以 swagger 进行记录。
  • 您无法按照您尝试的方式进行操作。请让它工作并告诉我。提前致谢。
  • 我相信您的回答仅适用于 asp.net 核心。在常规 asp.net 中没有我知道的 [FromQuery] 属性。
  • MVC 6 支持 [FromQuery]。你用的是什么版本?
  • nuget 版本是 5.2.7(由于 .NET 4.6)。
【解决方案2】:

我无法弄清楚如何通过模型绑定来做到这一点,但我能够使用动作过滤器来完成同样的事情。

这是我使用的代码(请注意,每个操作仅支持一个空模型,但如果需要,可以轻松修复):

public class NullModelActionFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext context)
    {
        object value = null;
        string modelName = string.Empty;

        // are there any null models?
        if (context.ActionArguments.ContainsValue(null))
        {
            // Yes => iterate over all arguments to find them.
            foreach (var arg in context.ActionArguments)
            {
                // Is the argument null?
                if (arg.Value == null)
                {
                    // Search the parameter bindings to find the matching argument....
                    foreach (var parameter in context.ActionDescriptor.ActionBinding.ParameterBindings)
                    {
                        //  Did we find a match?
                        if (parameter.Descriptor.ParameterName == arg.Key)
                        {
                            // Yes => Does the type have the 'Default' attribute?
                            var type = parameter.Descriptor.ParameterType;
                            if (type.GetCustomAttributes(typeof(DefaultAttribute), false).Length > 0)
                            {
                                // Yes => need to instantiate it
                                modelName = arg.Key;
                                var constructor = parameter.Descriptor.ParameterType.GetConstructor(new Type[0]);
                                value = constructor.Invoke(null);

                                // update the model state
                                context.ModelState.Add(arg.Key, new ModelState { Value = new ValueProviderResult(value, value.ToString(), CultureInfo.InvariantCulture) });
                            }
                        }
                    }
                }
            }

            // update the action arguments
            context.ActionArguments[modelName] = value;
        }
    }
}

我像这样创建了一个 DefaultAttribute 类:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class DefaultAttribute : Attribute
{
}

然后我将该属性添加到我的描述符类中:

[Default]
public class DescriptorModel
{
    public bool Fiction { get; set; } = false;

    // other properties with default arguments here
}

最后在

中注册了动作过滤器
public void Configure(IAppBuilder appBuilder)
{
    var config = new HttpConfiguration();
    // lots of configuration here omitted
    config.Filters.Add(new NullModelActionFilter());
    appBuilder.UseWebApi(config);
}

我绝对认为这是一个 hack(我认为我真的应该通过模型绑定来做到这一点),但它完成了我需要做的与 ASP.NET(不是核心)/WebApi2/.NET 的约束框架,因此希望其他人能从中受益。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-10
    • 2018-12-30
    • 1970-01-01
    相关资源
    最近更新 更多