通过[BindRequired]特性标识属性,我们基于表单的请求且未给出属性id的值,此时属性未绑定上且验证未通过,符合我们预期。接下来我们再来看看【FromBody】特性标识的请求,代码就不给出了,我们只是在对象上加上了[FromBody]而已,我们看看最终结果。
此时从表面上看好像达到了我们的预期,在这里即使我们对属性id不指定【BindRequired】特性,结果也是一样验证未通过,这是为何,因为默认情况下,在.NET Core中对于【FromBody】特性标识的对象不可为空,内置进行了处理,我们进行如下设置允许为空。
services.AddMvc(options=> { options.AllowEmptyInputInBodyModelBinding = true; }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
我们进行上述设置后,我们不给定属性id的值,肯定会验证通过对不对,那我们接下来再给定一个属性Age呢,然后发出请求不包含Age属性,如下
public class Customer { [BindRequired] public int Id { get; set; } [BindRequired] public int Age { get; set; } }
到这里我们发现我们对属性Age添加了【BindRequired】特性,此时验证却是通过的,我们再加思考一番,或许是我们给定的属性Age是int有默认值为0,所以验证通过,好想法,你可以继续添加一个字符串类型的属性,然后添加【BindRequired】特性,同时最后请求中不包含该属性,此时结果依然是验证通过的(不信自己试试)。
此时我们发现通过[FromBody]特性标识的请求,我们将默认对象不可空的情况排除在外,说明[BindRequired]特性标识的属性对[FromBody]特性标识的请求无效,同时呢,我们转到[BindRequired]特性的定义有如下解释:
// 摘要:
// Indicates that a property is required for model binding. When applied to a property, the model binding system requires a value for that property. When applied to
// a type, the model binding system requires values for all properties that type defines.
翻译过来不难理解,当我们通过[BindRequired]特性标识时,说明在模型绑定时属性是必须给出的,当应用到属性时,要求模型绑定系统必须验证此属性的值必须要给出,当应用到类型时,要求模型绑定系统必须验证类型中定义的所有属性必须有值。这个解释让我们无法信服,对于基于URL或者基于表单的请求和【FromBody】特性的请求明显有区别,但是定义却是一概而论。到这里我们遗漏到了一个【Required】特性,我们添加一个Address属性,然后请求中不包含Address属性,
public class Customer { [BindRequired] public int Id { get; set; } [BindRequired] public int Age { get; set; } [Required] public string Address { get; set; } }
从上图看出使用【FromBody】标识的请求,通过Required特性标识属性也符合预期,当然对于URL和表单请求也符合预期,在此不再演示。我并未看过源码,我大胆猜测下是否是如下原因才有其区别呢(个人猜测)
解释都在强调模型绑定系统,所以在.NET Core中出现的【BindNever】和【BindRequired】特性专为.NET Core MVC模型绑定系统而设计,而对于【FromBody】特性标识后,因为其进行属性的序列化和反序列化与Input Formatter有关,比如通过JSON.NET,所以至于属性的忽略和映射与否和我们使用序列化和反序列化的框架有关,由我们自己来定义,比如使用JSON.NET则属性忽略使用【JsonIgnore】。
所以说基于【FromBody】特性标识的请求,是否映射,是否必须由我们使用的序列化和反序列化框架决定,在.NET Core中默认是JSON.NET,所以对于如上属性是否必须提供,我们需要使用JSON.NET中的Api,比如如下。
public class Customer { [JsonProperty(Required = Required.Always)] public int Id { get; set; } [JsonProperty(Required = Required.Always)] public int Age { get; set; } }