如何在FluentValidation中创建警告消息

时间:2016-05-19 11:50:38

标签: c# asp.net-mvc unobtrusive-validation fluentvalidation

我想基于枚举类型为验证消息添加样式。 FluentValidation 可以使用WithState方法为邮件添加自定义状态。根据使用的枚举,它会在HTML中为该消息添加一个类,所以稍后我可以为它添加样式。

模型验证器类:

public class SampleModelValidator : AbstractValidator<SampleModelValidator>
{
    public SampleModelValidator()
    {
        RuleFor(o => o.Age)).NotEmpty()
                // Using custom state here
                .WithState(o => MsgTypeEnum.WARNING)
                .WithMessage("Warning: This field is optional, but better fill it!");
    }
}

控制器操作方法:

[HttpPost]
public ActionResult Submit(SampleModel model)
{
    ValidationResult results = this.validator.Validate(model);
    int warningCount = results.Errors
                .Where(o => o.CustomState?.ToString() == MsgTypeEnum.WARNING.ToString())
                .Count();
    ...
}

我注意到ASP.NET MVC默认使用unobtrusive.js并为每条错误消息添加了类.field-validation-error。所以我想需要以某种方式覆盖这种逻辑。

如何根据提供的枚举类型为验证消息添加样式?

1 个答案:

答案 0 :(得分:0)

我想出了如何实现这一点。首先需要创建一个html帮助器,它将构建html标签并为它们添加必要的类。此帮助程序允许为一个字段/属性显示具有不同类型的多个消息。

伟大的文章解释了如何做到这一点,可能是那里唯一的一个!
http://www.pearson-and-steel.co.uk/

Html Helper方法

public static MvcHtmlString ValidationMessageFluent<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, int? itemIndex = null)
{
    List<ValidationFailure> validationFailures = html.ViewData["ValidationFailures"] as List<ValidationFailure>;
    string exprMemberName = ((MemberExpression)expression.Body).Member.Name;
    var priorityFailures = validationFailures.Where(f => f.PropertyName.EndsWith(exprMemberName));

    if (priorityFailures.Count() == 0)
    {
        return null;
    }

    // Property name in 'validationFailures' may also be stored like this 'SomeRecords[0].PropertyName'
    string propertyName = itemIndex.HasValue ? $"[{itemIndex}].{exprMemberName}" : exprMemberName;

    // There can be multiple messages for one property
    List<TagBuilder> tags = new List<TagBuilder>();
    foreach (var validationFailure in priorityFailures.ToList())
    {
        if (validationFailure.PropertyName.EndsWith(propertyName))
        {
            TagBuilder span = new TagBuilder("span");
            string customState = validationFailure.CustomState?.ToString();

            // Handling the message type and adding class attribute
            if (string.IsNullOrEmpty(customState))
            {
                span.AddCssClass(string.Format("field-validation-error"));
            }
            else if (customState == MsgTypeEnum.WARNING.ToString())
            {
                span.AddCssClass(string.Format("field-validation-warning"));
            }

            // Adds message itself to the html element
            span.SetInnerText(validationFailure.ErrorMessage);
            tags.Add(span);
        }
    }

    StringBuilder strB = new StringBuilder();
    // Join all html tags togeather
    foreach(var t in tags)
    {
        strB.Append(t.ToString());
    }

    return MvcHtmlString.Create(strB.ToString());
}

此外,内部控制器操作方法需要获取验证器错误并将其存储在会话中。

控制器操作方法

// In controller you also need to set the ViewData. I don't really like to use itself
// but did not found other solution
[HttpPost]
public ActionResult Submit(SampleModel model)
{
   IList<ValidationFailure> errors = this.validator.Validate(model).Errors;
   ViewData["ValidationFailures"] = errors as List<ValidationFailure>;

    ...
}

只需在视图中使用该html助手。

<强> 查看

// In html view (using razor syntax)
@for (int i = 0; i < Model.SomeRecords.Count(); i++)
{
    @Html.ValidationMessageFluent(o => Model.SomeRecords[i].PropertyName, i)

    ...
}

仅在循环列表时才需要最后一个索引参数。