【问题标题】:unobtrusive client validation using fluentvalidation and asp.net mvc LessThanOrEqualTo not firing使用 fluentvalidation 和 asp.net mvc LessThanOrEqualTo 的不显眼客户端验证不触发
【发布时间】:2012-02-21 15:15:56
【问题描述】:

我有以下规则

第一个确实使用了不显眼的客户端验证,第二个没有

任何想法为什么?

RuleFor(x => x.StartDate)
    .LessThanOrEqualTo(x => x.EndDate.Value)
    .WithLocalizedMessage(() => CommonRes.Less_Than_Or_Equal_To, filters => CommonRes.Start_Date, filters => CommonRes.End_Date);

RuleFor(x => x.StartDate)
    .GreaterThanOrEqualTo(x => x.AbsoluteStartDate)
    .LessThanOrEqualTo(x => x.AbsoluteEndDate)
    .WithLocalizedMessage(() => CommonRes.Between, filters => CommonRes.Start_Date, filters => filters.AbsoluteStartDate, filters => filters.AbsoluteEndDate);

【问题讨论】:

  • 你确定第一个有效吗? LessThanOrEqualTo 不是 documentation 中列出的规则之一,因为客户端验证支持。您使用的是哪个版本的 FV?
  • 天哪!它是“LessThanOrEqualTo”,有什么解决方法吗?

标签: c# jquery asp.net-mvc asp.net-mvc-3 fluentvalidation


【解决方案1】:

LessThanOrEqualToGreaterThanOrEqualTo 规则均不受客户端验证支持,如 documentation 中所述。

这意味着,如果您想为它们进行客户端验证,您将需要编写自定义 FluentValidationPropertyValidator 并实现 GetClientValidationRules 方法,这将允许您注册自定义适配器并实现客户端验证逻辑它在javascript中。

如果您对如何实现这一点感兴趣,请联系我,我将提供一个示例。


更新

根据要求,我将尝试展示如何为LessThanOrEqualTo 规则实现自定义客户端验证的示例。这只是具有不可为空日期的特殊情况。为所有可能的情况编写这样的自定义客户端验证器当然是可能的,但需要付出更多的努力。

所以我们从视图模型和相应的验证器开始:

[Validator(typeof(MyViewModelValidator))]
public class MyViewModel
{
    [Display(Name = "Start date")]
    [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
    public DateTime StartDate { get; set; }

    public DateTime DateToCompareAgainst { get; set; }
}

public class MyViewModelValidator : AbstractValidator<MyViewModel>
{
    public MyViewModelValidator()
    {
        RuleFor(x => x.StartDate)
            .LessThanOrEqualTo(x => x.DateToCompareAgainst)
            .WithMessage("Invalid start date");
    }
}

然后是控制器:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new MyViewModel
        {
            StartDate = DateTime.Now.AddDays(2),
            DateToCompareAgainst = DateTime.Now
        };
        return View(model);
    }

    [HttpPost]
    public ActionResult Index(MyViewModel model)
    {
        return View(model);
    }
}

还有一个观点:

@model MyViewModel
@using (Html.BeginForm())
{
    @Html.Hidden("DateToCompareAgainst", Model.DateToCompareAgainst.ToString("yyyy-MM-dd"))

    @Html.LabelFor(x => x.StartDate)
    @Html.EditorFor(x => x.StartDate)
    @Html.ValidationMessageFor(x => x.StartDate)
    <button type="submit">OK</button>
}

到目前为止,所有这些都是标准内容。它可以工作,但没有客户验证。

第一步是写FluentValidationPropertyValidator

public class LessThanOrEqualToFluentValidationPropertyValidator : FluentValidationPropertyValidator
{
    public LessThanOrEqualToFluentValidationPropertyValidator(ModelMetadata metadata, ControllerContext controllerContext, PropertyRule rule, IPropertyValidator validator)
        : base(metadata, controllerContext, rule, validator)
    {
    }

    public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
    {
        if (!this.ShouldGenerateClientSideRules())
        {
            yield break;
        }

        var validator = Validator as LessThanOrEqualValidator;

        var errorMessage = new MessageFormatter()
            .AppendPropertyName(this.Rule.GetDisplayName())
            .BuildMessage(validator.ErrorMessageSource.GetString());

        var rule = new ModelClientValidationRule
        {
            ErrorMessage = errorMessage,
            ValidationType = "lessthanorequaldate"
        };
        rule.ValidationParameters["other"] = CompareAttribute.FormatPropertyForClientValidation(validator.MemberToCompare.Name);
        yield return rule;
    }
}

在配置我们的 FluentValidation 提供者时将在Application_Start 中注册:

FluentValidationModelValidatorProvider.Configure(x =>
{
    x.Add(typeof(LessThanOrEqualValidator), (metadata, context, rule, validator) => new LessThanOrEqualToFluentValidationPropertyValidator(metadata, context, rule, validator));
});

最后一点是客户端上的自定义适配器。因此,我们当然将 2 个脚本添加到我们的页面中,以启用不显眼的客户端验证:

<script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>

和自定义适配器:

(function ($) {
    $.validator.unobtrusive.adapters.add('lessthanorequaldate', ['other'], function (options) {
        var getModelPrefix = function (fieldName) {
            return fieldName.substr(0, fieldName.lastIndexOf(".") + 1);
        };

        var appendModelPrefix = function (value, prefix) {
            if (value.indexOf("*.") === 0) {
                value = value.replace("*.", prefix);
            }
            return value;
        }

        var prefix = getModelPrefix(options.element.name),
            other = options.params.other,
            fullOtherName = appendModelPrefix(other, prefix),
            element = $(options.form).find(":input[name=" + fullOtherName + "]")[0];

        options.rules['lessthanorequaldate'] = element;
        if (options.message != null) {
            options.messages['lessthanorequaldate'] = options.message;
        }
    });

    $.validator.addMethod('lessthanorequaldate', function (value, element, params) {
        var parseDate = function (date) {
            var m = date.match(/^(\d{4})-(\d{1,2})-(\d{1,2})$/);
            return m ? new Date(parseInt(m[1]), parseInt(m[2]) - 1, parseInt(m[3])) : null;
        };

        var date = parseDate(value);
        var dateToCompareAgainst = parseDate($(params).val());

        if (isNaN(date.getTime()) || isNaN(dateToCompareAgainst.getTime())) {
            return false;
        }

        return date <= dateToCompareAgainst;
    });

})(jQuery);

【讨论】:

  • @iwayneo,好的。我遇到的第一个问题是您在 LessThanOrEqualTo 验证器中比较的属性是否在表单中具有相应的字段,即您是在比较动态值(可以由用户在输入字段中更改)还是在呈现视图时此属性的值?
  • 被验证的属性是一个表单字段——我们要比较的属性是一个静态的不可编辑字段,在呈现视图的那一刻就知道了——我们将它存储在表单中的一个隐藏字段中跨度>
  • @iwayneo,您将要比较的值存储在隐藏字段中???等一下。什么会阻止用户使用例如 FireBug 将隐藏的值修改为他想要的任何日期,然后在输入字段中输入他想要的任何日期,从而使您的服务器端验证短路???这对您的应用程序来说是一个巨大的安全威胁。你怎么可能与来自客户端的值进行比较(在这种情况下是隐藏字段但很容易修改)?
  • 我可以交换它,所以我们不会那样做,这不是问题。这是内部帐户人员使用的内部应用程序,所以说实话并不真正担心黑客。但是你的观点是正确的,我可以用另一种方式给猫剥皮:) 如果它以另一种方式被检索到,我将如何继续?
  • @Gromer,如果您仔细查看我的Application_Start 方法,您会注意到我已经使用LessThanOrEqualValidator 规则注册了LessThanOrEqualToFluentValidationPropertyValidator 验证器。
【解决方案2】:

Darin 的示例中有一些过时的东西,所以这里有一个更新的示例,我有它可以进行数字比较。不过,您可以轻松调整它以进行日期比较:

Javascript:

(function ($)
{
    $.validator.addMethod("lessthanorequal", function(value, element, param)
    {
        return this.optional(element) || parseFloat(value) <= parseFloat(param);
    }, "Must be less than");

    $.validator.unobtrusive.adapters.add("lessthanorequal", ["field"], function (options)
    {
        options.rules["lessthanorequal"] = options.params.field;
        if (options.message) options.messages["lessthanorequal"] = options.message;
    });
})(jQuery);

C#

public class LessThanOrEqualPropertyValidator : FluentValidationPropertyValidator
{

    public LessThanOrEqualPropertyValidator(ModelMetadata metadata, ControllerContext controllerContext, PropertyRule rule, IPropertyValidator validator)
        : base(metadata, controllerContext, rule, validator)
    {
    }

    public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
    {
        if (!ShouldGenerateClientSideRules()) yield break;

        var formatter = new MessageFormatter().AppendPropertyName(Rule.PropertyName);
        string message = formatter.BuildMessage(Validator.ErrorMessageSource.GetString());
        var rule = new ModelClientValidationRule
        {
            ValidationType = "lessthanorequal",
            ErrorMessage = message
        };

         rule.ValidationParameters["field"] =  ((LessThanOrEqualValidator)Validator).ValueToCompare;
        yield return rule;
    }
}

Global.asax Application_Start:

FluentValidation.Mvc.FluentValidationModelValidatorProvider.Configure(x =>
{
    x.Add(typeof(LessThanOrEqualValidator), (metadata, context, description, validator) => new LessThanOrEqualPropertyValidator(metadata, context, description, validator));
});

所以现在任何使用 LessThanOrEqual 的数字规则都将在客户端进行验证。

【讨论】:

  • 客户端验证运行良好,但错误消息格式不正确'PurchaseDate' must be less than or equal to '{ComparisonValue}'.
  • 还有日期格式问题,我用的是dd/MM/yyyy模式,比如当前日期是20/10/2014。如果我填写 15/10/2014,它将显示 'PurchaseOrderDate' must be less than or equal to '{ComparisonValue}'.
【解决方案3】:

LessThanOrEqualToGreaterThanOrEqualTo 不支持开箱即用的客户端验证。

但是,支持InclusiveBetween。所以你可以使用InclusiveBetween

示例

RuleFor(x => x.StartDate)
    .InclusiveBetween(x.AbsoluteStartDate, x.AbsoluteEndDate)

有关supported clientside methods 的更多信息,请参阅文档。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多