ASP.NET Core [要求]非可空类型

时间:2018-06-18 12:58:31

标签: c# validation asp.net-core

Here,提出了如何验证不可空的必需类型的问题。

在我的情况下,提供的解决方案使得字段可以像以下一样可空。

[Required]
public int? Data { get; set; }

如果在请求中省略字段的情况下,如何更改行为而不是进行以下失败验证。

[Required]
public int Data { get; set; }

我尝试过自定义验证程序,但这些验证程序没有关于原始值的信息,只能看到默认的0值。我也尝试过自定义模型绑定器,但它似乎在整个请求模型的级别上工作,而不是想要的整数字段。我的活页夹实验看起来像这样:

public class RequiredIntBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType != typeof(int))
            throw new InvalidOperationException($"{nameof(RequiredIntBinder)} can only be applied to integer properties");

        var value = bindingContext.ValueProvider.GetValue(bindingContext.BinderModelName);
        if (value == ValueProviderResult.None)
        {
            bindingContext.Result = ModelBindingResult.Failed();
            return Task.CompletedTask;
        }

        return new SimpleTypeModelBinder(bindingContext.ModelType).BindModelAsync(bindingContext);
    }
}

public class RequiredIntBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (context.Metadata.ModelType == typeof(int))
        {
            return new BinderTypeModelBinder(typeof(RequiredIntBinder));
        }

        return null;
    }
}

并在mvc中注册了

options.ModelBinderProviders.Insert(0, new RequiredIntBinderProvider());

但从不使用模型装订器。我觉得我可能很接近,但无法连接最后的点。

4 个答案:

答案 0 :(得分:3)

  

不可空的必需类型。

你没有。它要么是必需的 - 那么它没有任何意义可以为空 - 或者它不是必需的,那么你可以理智,但是要求它是没有意义的。

属性总是针对整个请求。您遇到了逻辑问题,因为您尝试使用它们并非按预期使用。

如果是可选的,用户应该实际提交补丁,而不是put / post。

答案 1 :(得分:2)

请求中的所有内容都只是一个字符串。模型绑定器将请求正文中的键与属性名称进行匹配,然后尝试将它们强制转换为适当的类型。如果属性未发布或使用空字符串发布,那么在尝试转换为int时,这显然会失败。因此,您最终得到了该类型的默认值。如果int0,则int?的默认值为null

只有在此绑定过程完成后才会验证模型。请记住,您正在验证模型而不是帖子正文。验证帖子正文没有合理的方法,因为它只是一堆键值对字符串。因此,如果int属性是必需的但未发布的,则值为0,这是int的完全有效值,并且验证得到满足。在int?的情况下,值为null是有效的int,因此验证失败。这就是为什么nullable是必需的,如果你想要一个非可空类型有一个值。这是将空值与简单的“默认”值区分开来的唯一方法。

如果您正在使用视图模型,那么这应该不是问题。您可以使用必需属性绑定到可空int,并且如果模型状态有效,则可以确保具有值,尽管可以为空。然后,您可以将其映射到实体上的直线int。这是处理事情的正确方法。

答案 2 :(得分:0)

有办法做到这一点,至少对我有用,对非空类型尝试使用[BindRequired]

答案 3 :(得分:0)

解决json请求的方法

创建合同解析器

public class RequiredPropertiesContractResolver : DefaultContractResolver
{
    protected override JsonObjectContract CreateObjectContract(Type objectType)
    {
        var contract = base.CreateObjectContract(objectType);

        foreach (var contractProperty in contract.Properties)
        {
            if (contractProperty.PropertyType.IsValueType
                && contractProperty.AttributeProvider.GetAttributes(typeof(RequiredAttribute), inherit: true).Any())
            {
                contractProperty.Required = Required.Always;
            }
        }

        return contract;
    }
}

,然后将其分配给SerializerSettings

services.AddMvc()
        .AddJsonOptions(jsonOptions =>
        {
            jsonOptions.SerializerSettings.ContractResolver = new RequiredPropertiesContractResolver();
        });

如果json缺少值,则ModelState对于具有[Required]属性的不可为空的属性无效。


示例

Json身体

var jsonBody = @"{ Data2=123 }"

对于模型无效

class Model
{
    [Required]
    public int Data { get; set; }

    public int Data2 { get; set; }
}