是否可以在 FluentValidation 中重用默认消息?

Is it possible to reuse default message in FluentValidation?

是否可以重用验证器提供的默认消息并附加一些数据?例如,MaxLength 验证器 returns 消息,如“ 的长度必须为 n 个字符或更少。您输入了 m 个字符”。我需要的是获得类似“Id 123456: < default validator message >”的信息。 WithMessage 只是覆盖了整个消息,每个验证器的复制粘贴输出都是无稽之谈。

是的,您可以使用自定义 MessageBuilder 来执行此操作。这是库的一项高级功能,我还没有机会记录它,但它允许您在生成消息时拦截消息并对其进行调整。

public static class MyExtensions {
    public static IRuleBuilderOptions<T, TProperty> WrapDefaultMessage<T, TProperty>(this IRuleBuilderOptions<T, TProperty> ruleBuilder, string template) {
        return ruleBuilder.Configure(rule => {
            // A rule can only have a single message builder.
            // If it already has one, cache it and call it from within our new one below
            // (essentially chaining them together).
            var originalMessageBuilder = rule.MessageBuilder;

            rule.MessageBuilder = context => {
                // Generate the default message.
                string message = originalMessageBuilder?.Invoke(context) ?? context.GetDefaultMessage();

                // Now add that message *back* into the message formatter as a custom placeholder value.
                context.MessageFormatter.AppendArgument("DefaultMessage", message);

                // Now construct new message using the supplied template,
                // which allows the new DefaultMessage placeholder to be used.
                return context.MessageFormatter.BuildMessage(template);
            };
        });
    }
}

...然后您可以像这样使用它:

RuleFor(x => x.Name).NotNull().WrapDefaultMessage("ID 12345 {DefaultMessage}");

...如果您的 ID“12345”来自对象上的另一个 属性,那么您可以修改代码以进行回调而不是允许您在消息中访问它的字符串:

public static class MyExtensions {
    // Note that the parameter is now a Func<T, string> instead of a string.
    public static IRuleBuilderOptions<T, TProperty> WrapDefaultMessage<T, TProperty>(this IRuleBuilderOptions<T, TProperty> ruleBuilder, Func<T, string> templateBuilder) {
        return ruleBuilder.Configure(rule => {
            // A rule can only have a single message builder.
            // If it already has one, cache it and call it from within our new one below
            // (essentially chaining them together).
            var originalMessageBuilder = rule.MessageBuilder;

            rule.MessageBuilder = context => {
                // Generate the default message.
                string message = originalMessageBuilder?.Invoke(context) ?? context.GetDefaultMessage();

                // Now add that message *back* into the message formatter as a custom placeholder value.
                context.MessageFormatter.AppendArgument("DefaultMessage", message);

                // Now construct new message using the supplied template,
                // which allows the new DefaultMessage placeholder to be used.
                return context.MessageFormatter.BuildMessage(templateBuilder((T)context.InstanceToValidate));
            };
        });
    }
}

...然后您可以像这样使用它:

RuleFor(x => x.Name).NotNull().WrapDefaultMessage(x => $"Id {x.Id} {{DefaultMessage}}");

(请注意,在像这样的内插字符串中使用时,您必须 double-escape 占位符括号)。