如何将ASP.NET Core Web API属性验证集中在具有类似属性的多个DTO上



有没有一种方法可以在多个DTO之间集中对同一属性名称进行模型验证?

例如,如果我有以下类要用作Web API操作中的请求主体。

public class RegisterRequest
{
[Required]
[EmailAddress]
public string EmailAddress { get; set; } = null!;
[Required]
[MinLength(8)]
[RegularExpression(UserSettings.PasswordRegex)]
public string Password { get; set; } = null!;
[Required]
[MaxLength(100)]
public string DisplayName { get; set; } = null!;
}
public class UserProfileRequest
{
[Required]
public int UserId { get; set; }
[Required]
[MaxLength(100)]
public string DisplayName { get; set; } = null!;
[Range(3, 3)]
public string? CCN3 { get; set; }
}

我可以将属性验证集中在DisplayName上吗?复制属性违反了单一责任原则。我相信我可以使用IFilterFactory并减少属性的使用来实现集中验证。

我选择使用自定义ActionFilterAttribute来实现验证的集中化。以下示例用于验证国家/地区代码(CCN3)。

CountryCodeValidationAttribute.cs-要应用于属性的自定义属性(不包含逻辑)

[AttributeUsage(AttributeTargets.Property)]
public class CountryCodeValidationAttribute : Attribute
{
}

CountryCodeValidationActionFilter.cs-自定义操作筛选器,支持依赖项注入并在属性上查找自定义属性。在我的情况下,我将返回标准的无效模型坏请求响应。

public class CountryCodeValidationActionFilter : ActionFilterAttribute
{
private readonly ICountryService countryService;
private readonly IOptions<ApiBehaviorOptions> apiBehaviorOptions;
public CountryCodeValidationActionFilter(
ICountryService countryService,
IOptions<ApiBehaviorOptions> apiBehaviorOptions)
{
this.countryService = countryService;
this.apiBehaviorOptions = apiBehaviorOptions;
}
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var actionArguments = context.ActionArguments;
foreach (var actionArgument in actionArguments)
{
if (actionArgument.Value == null) continue;
var propertiesWithAttributes = actionArgument.Value
.GetType()
.GetProperties()
.Where(x => x.GetCustomAttributes(true).Any(y => y.GetType() == typeof(CountryCodeValidationAttribute)))
.ToList();
foreach (var property in propertiesWithAttributes)
{
var value = property.GetValue(actionArgument.Value)?.ToString();
if (value != null && await countryService.GetCountryAsync(value) != null) await next();
else
{
context.ModelState.AddModelError(property.Name, "Must be a valid country code");
context.Result = apiBehaviorOptions.Value.InvalidModelStateResponseFactory(context);
}
}
}
await base.OnActionExecutionAsync(context, next);
}
}

Program.cs-注册自定义操作筛选器。

builder.Services.AddMvc(options =>
{
options.Filters.Add(typeof(CountryCodeValidationActionFilter));
});

UserProfile.cs-将[CountryCodeValidation]属性应用于CountryCode属性。

public class UserProfile
{
[Required]
[MaxLength(100)]
public string DisplayName { get; set; } = null!;
[CountryCodeValidation]
public string? CountryCode { get; set; }
}

我可以采用同样的方法,并将其应用于DisplayName属性,为其创建集中验证。

最新更新