.NET 6 OpenAPI中如何指定POST请求类的必填属性?
.NET 6正式落地可空引用类型(NRT)特性后,ASP.NET Core的模型绑定、OpenAPI(Swagger)的字段必填性识别已经和NRT规则做了深度对齐,之前老版本用[Required]打标记的思路已经不适合作为默认方案,你提到的两种写法都存在明显问题。
首先明确框架默认的识别逻辑:当项目开启可空引用类型时,带?标记的引用类型属性会被默认识别为可选属性,比如你给出的示例:
public class MyPostRequest { public SomeType? MyOptionalProperty { get; set; } }
这个属性会被OpenAPI自动标记为可选,模型绑定阶段也不会强制要求请求携带该字段,这个行为是符合预期的。
你提到的两种写法的问题
先看第一种:用编译指令屏蔽CS8618警告的写法
public class MyPostRequest { #pragma warning disable CS8618 public SomeType MyRequiredProperty { get; set; } #pragma warning restore CS8618 }
这种写法本质是回避问题:你只是关掉了编译器对“非空属性未初始化”的警告,既没有给ASP.NET Core、OpenAPI生成器传递明确的必填信号(部分版本的组件不会把这种属性识别为必填),也放弃了编译器的空值静态检查,后续代码很容易出现意料之外的空引用异常。
再看第二种:给可空属性加[Required]的写法
public class MyPostRequest { [Required] public SomeType? MyRequiredProperty { get; set; } }
这种写法语义完全矛盾:你一方面用?告诉编译器、框架这个属性可能为null,另一方面又用[Required]声明它不能为null,不仅会让代码的可空语义混乱,导致静态空检查失效,还会让后续维护的开发者产生误解。
官方推荐的规范写法
核心原则非常简单:必填属性声明为非可空引用类型,可选属性声明为带?的可空引用类型,从语义上和可空规则保持一致,不需要额外加[Required]框架就能自动识别。
- 如果你用的是.NET 7及以上版本(C# 11+),直接用
required关键字是最优解:
public class MyPostRequest { // 必填属性:非可空类型+required关键字 public required SomeType MyRequiredProperty { get; set; } // 可选属性:带?的可空类型 public SomeType? MyOptionalProperty { get; set; } }
这种写法下,编译器会强制要求类初始化时必须给必填属性赋值,从编译期就避免空值问题;同时ASP.NET Core模型绑定会自动将其标记为必填,请求缺字段时自动返回400校验错误,Swagger/OpenAPI也会自动把该字段归入required列表,不需要任何额外配置。
- 如果你还在使用.NET 6(C# 10,没有
required关键字),用null原谅运算符初始化属性即可,不要用编译指令屏蔽警告:
public class MyPostRequest { // 必填属性:非可空类型,用null!明确告知编译器该值会由模型绑定负责赋值 public SomeType MyRequiredProperty { get; set; } = null!; // 可选属性:带?的可空类型 public SomeType? MyOptionalProperty { get; set; } }
这里的= null!是明确告诉编译器:我知道这个属性初始值是null,但运行时模型绑定会给它赋值,保证不会出现空引用,比全局屏蔽CS8618警告的作用范围更小,语义更清晰。
只有当你需要自定义校验规则(比如自定义错误提示、匹配特殊正则等)时,才需要额外添加[Required]特性,此时属性依然保持非可空声明即可,不需要加?:
public class MyPostRequest { [Required(ErrorMessage = "必须提交MyRequiredProperty字段")] public SomeType MyRequiredProperty { get; set; } = null!; }
内容的提问来源于stack exchange,提问作者Carsten Gehling

