启用可空引用类型时非可空属性导致Action方法未执行
问题现象
启用Version 0或Version 1对应的代码配置时,在Swagger-UI中保持所有文本框为空点击Execute按钮会触发错误,经排查ContactsController中的Get方法体始终未被调用。切换为其余版本配置时,Get方法可正常执行。
复现代码如下:
public class Contact { public string Name { get; set; } // Version 0 public string Name { get; set; } = null!; // Version 1 // public string Name { get; set; } = String.Empty; // Version 2 // public string? Name { get; set; } // Version 3 // public string? Name { get; set; } = String.Empty;// Version 4 public int Age { get; set; } } [ApiController] [Route("api/[controller]/[action]")] public class ContactsController : ControllerBase { [HttpGet] public ActionResult Get([FromQuery] Contact c) { if (c.Name is null) return Ok("Name is null"); if (c.Name == String.Empty) return Ok("Name is empty"); return Ok("End reached"); } }
产生原因
该问题是ASP.NET Core模型验证默认规则和C#可空引用类型(NRT)特性联动导致的:
- 项目开启可空引用类型检查后,所有未标注
?的引用类型属性,会被框架自动判定为非空必填字段,要求请求必须传入对应有效值,否则会在模型绑定阶段直接返回400参数错误,不会进入控制器方法逻辑。 - Version 0和Version 1中的
Name属性均为未加?的string类型,其中Version 1的=null!写法仅用于绕过编译器的空引用警告,不会改变框架对字段必填性的判定。 - Swagger空表单提交时,请求Query参数中没有携带
Name字段值,模型绑定阶段直接判定必填字段缺失,触发自动校验拦截,因此Get方法永远不会执行。 - 其余版本可正常运行的原因:
- Version 2为
Name设置了空字符串默认值,模型绑定未获取到传入值时会直接使用默认值填充,不会触发校验失败 - Version 3、Version 4的
Name带?标记,被框架识别为可空字段,请求未传值也不会触发验证拦截
- Version 2为
如果需要保留Version 0/1的属性写法,同时让接口正常处理空参数请求,可以在服务配置中关闭非空引用类型的隐式必填校验:
builder.Services.AddControllers(options => { options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true; });
内容的提问来源于stack exchange,提问作者Display Name
相关产品推荐
相关产品推荐

