您现在的位置是:首页 >技术教程 >“The xxx field is required“, Nullable引发的bug网站首页技术教程
“The xxx field is required“, Nullable引发的bug
0.背景
最近接到一个报告的bug,有一个前端的功能不好用了。这个项目是由前端和后台服务组成的,前端用的angular,服务用的是.net6.0。从前端开始排查,发现有个api的返回错误,返回中报告 “The xxx field is required”,这个field在请求中被设置未了null,而查看了service里面的模型代码,这个field并没有被设置为required,而这段代码一直没有修改。
request的内容
{
"name_guid": "B0AE3B10-A824-4D1E-8F2D-160A5FC0F577",
"items": [
{
"item1": "AAA",
"item2": "AA1",
"item3": null,
},
{
"item1": "BBB",
"item2": "BB1",
"item3": null,
},
{
"item1": "CCC",
"item2": "CC1",
"item3": null,
}
]
}
返回的内容
status:400Bad Request
{
"errors": {
"items[0].item3": [
"The item3 field is required."
],
"items[1].item3": [
"The item3 field is required."
],
"items[2].item3": [
"The item3 field is required."
]
},
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "00-xxxxxxxxxxxxxxxxx-00"
}
再来看看服务端的定义
public class itemdef
{
[Required]
[JsonProperty(PropertyName = "item1")]
public string item1{ get; set; }
[JsonProperty(PropertyName = "item2")]
public string item2 { get; set; } = string.Empty;
[JsonProperty(PropertyName = "item3")]
public string item3 { get; set; } = string.Empty;
}
public class theGetRequest
{
[JsonProperty(PropertyName = "name_guid")]
[StringLength(36)]
public string JobGuid { get; set; }
[JsonProperty(PropertyName = "channels")]
public itemdef[] items{ get; set; }
}
1.排查
1.1 跟代码
首先想到的跟代码,因为能够稳定复现,所以应该好查。
首先在代码库中搜索报错信息,查is required,居然没有查到。
那就在入口函数处打断点,于是在controller中的处理函数入口处打断点,结果没进去。那就有两种可能,一种是断点位置打错了,是其他函数处理的这个请求,另一个就是还没进这个函数就返回了。
于是修改了请求,把null改成一个字符串,断点命中,说明位置是对的,只是还没进这个函数就返回了。
查了一下资料,这个是WebApi的模型验证,那一定是模型定义的问题。
但是查了一下代码,并没有发现近期有相关的修改。
1.2 分析
再回头看一下这个现象,返回值显示item3 是必须的,但是在模型中并没有设置[required]属性,那么是默认就是required?那么怎么设置为非required呢?代码一样,为什么之前可以呢?
询问了chatgpt,得到的答案是:
在 ASP.NET Web API 中,默认情况下,所有值类型的属性都被视为必需的,即使您没有显式地将它们标记为 [Required]。因此,如果您想要将一个值类型的属性设置为非必需的,您需要将其声明为可空值类型(例如 int?、double?、DateTime? 等)。
尝试给item3的定义加上“?”,变成可空的,编译运行,居然成功了。
那么为什么之前没改代码却可以不加“?”呢?
1.2 查变更
要回答这个问题就得定位到是那条修改引起的这个问题。用二分法查找。最后终于找到了引起这个bug的变更。我们把一部分代码从service中提取出来放到了一个common的dll中,以便与其他服务共享,但是代码内容基本没变。那么是什么原因导致了这个行为上的不同呢?
2.水落石出
经过了调查,发现common的project设置中有一行
enable
而在原来service的project设置中却没有。
答案找到了,由于service没有设置nullable为enable,所以item3是可以接受null值的,但是移到common中之后,由于设置了nullable,导致item3不能接受null值,才导致了模型检查失败返回。
3.另一处值得注意的地方
注意到了item3的模型定义中有一个“= string.Empty;”,这应该是设置默认值啦,那是这个默认值没生效吗?
进行了另一个尝试,把request中的item3=null删掉,就是没有这个field,发送请求,没有报错。
那么说明设置默认值生效了。
4.总结
从这件事情上总结出两个经验:
4.1 模型验证中的nullable
参考内容:Model validation in ASP.NET Core MVC and Razor Pages
4.2 request中的=null和无该参数的区别
服务端在处理=null和无参数是不同的,当=null的时候会认为有该参数,值为null,不会触发默认值赋值,而如果没有这个参数则会使用默认值。