如何在FluentValidation中实现WithMessageAsync异步消息翻译
实现FluentValidation的异步错误消息链式调用方案
要实现WithMessageAsync并复用内置验证规则,核心思路是通过包装内置验证器,重写其异步消息获取逻辑,同时保留链式调用的体验。以下是具体实现步骤:
1. 创建异步消息包装验证器
这个类负责包裹FluentValidation的内置验证器,替换其错误消息的获取逻辑为异步方式:
public class AsyncMessagePropertyValidator<T, TProperty> : PropertyValidator<T, TProperty> { private readonly PropertyValidator<T, TProperty> _innerValidator; private readonly Func<ValidationContext<T>, Task<string>> _messageProvider; public AsyncMessagePropertyValidator(PropertyValidator<T, TProperty> innerValidator, Func<ValidationContext<T>, Task<string>> messageProvider) { _innerValidator = innerValidator; _messageProvider = messageProvider; RuleSets = innerValidator.RuleSets; } public override async Task<bool> IsValidAsync(ValidationContext<T> context, TProperty value, CancellationToken cancellationToken) { return await _innerValidator.IsValidAsync(context, value, cancellationToken); } protected override async Task<string> GetErrorMessageAsync(ValidationContext<T> context, TProperty value) { return await _messageProvider(context); } public override bool IsValid(ValidationContext<T> context, TProperty value) { return _innerValidator.IsValid(context, value); } protected override string GetErrorMessage(ValidationContext<T> context, TProperty value) { throw new InvalidOperationException("请使用异步验证方法调用此规则"); } }
2. 编写扩展方法实现链式调用
通过扩展IRuleBuilderOptions,添加WithMessageAsync方法,将内置验证器替换为我们的包装器:
public static class FluentValidationAsyncExtensions { public static IRuleBuilderOptions<T, TProperty> WithMessageAsync<T, TProperty>( this IRuleBuilderOptions<T, TProperty> rule, Func<ValidationContext<T>, Task<string>> messageProvider) { var innerValidator = rule.CurrentValidator as PropertyValidator<T, TProperty>; if (innerValidator == null) { throw new InvalidOperationException("WithMessageAsync仅适用于属性验证器"); } rule.ClearValidators(); rule.SetValidator(new AsyncMessagePropertyValidator<T, TProperty>(innerValidator, messageProvider)); return rule; } // 重载:结合翻译服务的快捷方式 public static IRuleBuilderOptions<T, TProperty> WithMessageAsync<T, TProperty>( this IRuleBuilderOptions<T, TProperty> rule, ITranslationService translationService, string translationKey, params object[] formatArgs) { return rule.WithMessageAsync(async context => { var translatedText = await translationService.GetTranslationAsync(translationKey, context.CultureInfo); return string.Format(translatedText, formatArgs); }); } }
3. 实际使用示例
假设你有一个从数据库获取译文的ITranslationService,注入到验证器后即可像原生WithMessage一样链式调用:
public class UserValidator : AbstractValidator<User> { private readonly ITranslationService _translationService; public UserValidator(ITranslationService translationService) { _translationService = translationService; // 基础用法:复用NotEmpty规则,异步获取翻译消息 RuleFor(x => x.FirstName) .NotEmpty() .WithMessageAsync(_translationService, "FirstName.Empty"); // 自定义异步逻辑:针对邮箱格式验证设置异步消息 RuleFor(x => x.Email) .NotEmpty() .WithMessageAsync(_translationService, "Email.Empty") .EmailAddress() .WithMessageAsync(async ctx => await _translationService.GetTranslationAsync("Email.InvalidFormat", ctx.CultureInfo)); } }
关键注意点
- 必须使用异步验证方法:在Controller或服务中调用
await _validator.ValidateAsync(userModel),同步验证会抛出异常(可根据需求修改包装器的同步方法逻辑)。 - 多规则独立设置:如果一个字段有多个验证规则(如邮箱的NotEmpty和EmailAddress),需为每个规则单独调用
WithMessageAsync。 - 上下文支持:
ValidationContext包含当前模型、文化信息等,可用于多语言翻译场景。
内容的提问来源于stack exchange,提问作者AlwaysNeedingHelp
相关产品推荐
相关产品推荐

