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());
但从不使用模型装订器。我觉得我可能很接近,但无法连接最后的点。
答案 0 :(得分:3)
不可空的必需类型。
你没有。它要么是必需的 - 那么它没有任何意义可以为空 - 或者它不是必需的,那么你可以理智,但是要求它是没有意义的。
属性总是针对整个请求。您遇到了逻辑问题,因为您尝试使用它们并非按预期使用。
如果是可选的,用户应该实际提交补丁,而不是put / post。
答案 1 :(得分:2)
请求中的所有内容都只是一个字符串。模型绑定器将请求正文中的键与属性名称进行匹配,然后尝试将它们强制转换为适当的类型。如果属性未发布或使用空字符串发布,那么在尝试转换为int时,这显然会失败。因此,您最终得到了该类型的默认值。如果int
为0
,则int?
的默认值为null
。
只有在此绑定过程完成后才会验证模型。请记住,您正在验证模型而不是帖子正文。验证帖子正文没有合理的方法,因为它只是一堆键值对字符串。因此,如果int
属性是必需的但未发布的,则值为0
,这是int的完全有效值,并且验证得到满足。在int?
的情况下,值为null
,不是有效的int,因此验证失败。这就是为什么nullable是必需的,如果你想要一个非可空类型有一个值。这是将空值与简单的“默认”值区分开来的唯一方法。
如果您正在使用视图模型,那么这应该不是问题。您可以使用必需属性绑定到可空int,并且如果模型状态有效,则可以确保将具有值,尽管可以为空。然后,您可以将其映射到实体上的直线int。这是处理事情的正确方法。
答案 2 :(得分:0)
有办法做到这一点,至少对我有用,对非空类型尝试使用[BindRequired]
。
答案 3 :(得分:0)
创建合同解析器
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; }
}