ASP.NET Core Web API 入門心得 - 必填欄位驗證
TLDR
- 針對
Boolean等實值型別,應使用Nullable型別搭配[Required]屬性來實現必填驗證。 [BindRequired]僅適用於表單資料(Form Data),不適用於[FromBody]的 JSON/XML 資料。- 若需解決 Create 與 Update 共用 DTO 但驗證邏輯不同的問題,可透過自定義
RequiredForTypeAttribute實現。 - 使用自定義驗證屬性時,需搭配
ISchemaFilter同步調整 Swagger 文件,以確保 API 文件顯示正確的必填狀態。
[Required] 與 [BindRequired] 的差異與限制
在 ASP.NET Core 中,當資料繫結時,若參數未從來源找到對應值,系統會賦予其預設值。對於結構型別(Struct,如 Boolean),預設值(false)會導致無法判斷使用者是「未傳入值」還是「傳入 false」。
什麼情況下會遇到這個問題
當您使用 [FromBody] 接收 JSON 資料,且模型中包含 bool 等實值型別,希望強制要求前端必須傳入該欄位時。
解決方案
對於 [FromBody] 的情境,建議將屬性宣告為 Nullable 型別並搭配 [Required]:
csharp
public class Input {
[Required]
public bool? IsRequired { get; set; }
}注意事項
[BindRequired] 屬性僅適用於來自表單(Form)的資料繫結。根據官方文件,它不適用於要求本文(Request Body)中的 JSON 或 XML 資料,因為後者是由輸入格式器(Input Formatter)處理的。
處理 Create 與 Update 的共用 DTO 驗證
在實務開發中,常會遇到 Create 與 Update 共用 DTO 的情況,但兩者的必填欄位邏輯可能不同(例如 Update 時部分欄位為選填)。
什麼情況下會遇到這個問題
當 Update DTO 繼承自 Create DTO,但希望在 Update 情境下放寬某些欄位的 [Required] 限制時。
解決方案:自定義 RequiredForTypeAttribute
由於 Attribute 的 Inherited 屬性僅適用於 Class 與 Method,不適用於 Property,因此可透過自定義驗證屬性來判斷當前的驗證上下文(Context):
csharp
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public class RequiredForTypeAttribute : RequiredAttribute {
public Type[] TargetTypes { get; set; }
public RequiredForTypeAttribute(params Type[] targetTypes) {
TargetTypes = targetTypes ?? throw new ArgumentNullException(nameof(targetTypes));
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
if (!TargetTypes.Contains(validationContext.ObjectType) || IsValid(value)) {
return ValidationResult.Success;
}
string[] memberNames = validationContext.MemberName != null ? new string[] { validationContext.MemberName } : null;
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName), memberNames);
}
}使用方式如下:
csharp
public class CreateInput {
[RequiredForType(typeof(CreateInput))]
public bool? IsRequired { get; set; }
}
public class UpdateInput : CreateInput { }Swagger 文件同步
由於 [Required] 會影響 Swagger 的 Schema 顯示,若使用上述自定義屬性,必須實作 ISchemaFilter 來手動修正 Swagger 文件中的必填欄位標記:
csharp
public class RequiredForTypeSchemaFilter : ISchemaFilter {
public void Apply(OpenApiSchema schema, SchemaFilterContext context) {
if (schema.Properties is null) return;
foreach (PropertyInfo prop in context.Type.GetProperties()) {
RequiredForTypeAttribute attr = prop.GetCustomAttributes<RequiredForTypeAttribute>().FirstOrDefault();
if (attr is not null && !attr.TargetTypes.Contains(context.Type)) {
foreach (var schemaPropPair in schema.Properties) {
if (string.Equals(schemaPropPair.Key, prop.Name, StringComparison.OrdinalIgnoreCase)) {
schema.Required.Remove(schemaPropPair.Key);
break;
}
}
}
}
}
}

異動歷程
- 初版文件建立。