【问题标题】:How to validate Date in ClientSide using FluentValidation?如何使用 FluentValidation 在客户端验证日期?
【发布时间】:2013-08-09 08:07:11
【问题描述】:

问题

下面的代码在Server 端工作正常,而不是客户端。为什么?


当我提交表单时,控制转到BeAValidDate 函数来检查日期是否有效。有什么方法可以在不使用Fluent Validation 访问服务器的情况下Validate 日期?

脚本

<script src="jquery-1.7.1.min.js" type="text/javascript"></script>
<script src="jquery.validate.js" type="text/javascript"></script>
<script src="jquery.validate.unobtrusive.js" type="text/javascript"></script>

型号

public class PersonValidator : AbstractValidator<Person>
{
    public PersonValidator()
    {
        RuleFor(x => x.FromDate)
            .NotEmpty()
            .WithMessage("Date is required!")
            .Must(BeAValidDate)
            .WithMessage("Invalid Date");
    }

    private bool BeAValidDate(String value)
    {
        DateTime date;
        return DateTime.TryParse(value, out date);
    }
}

控制器

public class PersonController : Controller
{
    public ActionResult Index()
    {
       return View(new Person { FromDate = DateTime.Now.AddDays(2).ToString()});
    }

    [HttpPost]
    public ActionResult Index(Person p)
    {
        return View(p);
    }
}

查看

@using (Html.BeginForm("Index", "Person", FormMethod.Post))
{   
    @Html.LabelFor(x => x.FromDate)
    @Html.EditorFor(x => x.FromDate)
    @Html.ValidationMessageFor(x => x.FromDate)

    <input type="submit" name="Submit" value="Submit" />
}

【问题讨论】:

  • 您是否在 web.config 中激活了客户端和不显眼的验证?
  • 你设置 FluentValidationModelValidatorProvider 了吗?
  • @JTMon :与数据注释一起正常工作不显眼。我很想知道日期的客户端流利验证。
  • This question 和你的类似,这里你可以看到如何使用jquery.validate.js 来适应FluentValidation。这是另一个问题,in this answer 指的是此链接Integration with ASP.NET MVC,其中解释了如何集成和扩展某些验证并接受其他验证。在这里你可以看到 [NotEqual Fluent Validation 验证器与客户端验证](gist.github.com/michaeljacob

标签: asp.net-mvc asp.net-mvc-3 fluentvalidation


【解决方案1】:

使用大于或等于验证器的技巧。对我有用。

Global.asax - 应用程序启动事件

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

型号

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

    [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", 
                                                  ApplyFormatInEditMode = true)]
    public DateTime DateToCompareAgainst { get; set; }
}

规则

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

FluentValidationPropertyValidator

public class GreaterThenOrEqualTo : FluentValidationPropertyValidator
{
    public GreaterThenOrEqualTo(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 GreaterThanOrEqualValidator;

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

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

控制器动作方法

public ActionResult Index()
{
    var model = new MyViewModel
    {
        StartDate = DateTime.Now.AddDays(2),
        DateToCompareAgainst = default(DateTime)  //Default Date
    };
    return View(model);
}
[HttpPost]
public ActionResult Index(Practise.Areas.FluentVal.Models.MyViewModel p)
{
    return View(p);
}

查看

@using (Html.BeginForm("Index", "Person", FormMethod.Post, 
                                                new { id = "FormSubmit" }))
{   
    @Html.Hidden("DateToCompareAgainst", Model.DateToCompareAgainst);      
    @Html.LabelFor(x => x.StartDate)
    @Html.EditorFor(x => x.StartDate)
    @Html.ValidationMessageFor(x => x.StartDate)
    <button type="submit">
        OK</button>
}

脚本

<script src="jquery-1.4.1.min.js" type="text/javascript"></script>
<script src="jquery.validate.js" type="text/javascript"></script>
<script src="jquery.validate.unobtrusive.js" type="text/javascript"></script>
<script type="text/javascript">
    (function ($) {
        $.validator.unobtrusive.adapters.add('greaterthanorequaldate', 
                                             ['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['greaterthanorequaldate'] = element;
            if (options.message != null) {
                options.messages['greaterthanorequaldate'] = options.message;
            }
        });

        $.validator.addMethod('greaterthanorequaldate', 
                               function (value, element, params) {
            var date = new Date(value);
            var dateToCompareAgainst = new Date($(params).val());

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

    })(jQuery);
</script>

【讨论】:

  • 所有这些都是为了比较一个数字。
【解决方案2】:

关于您的设置以及哪些设置不起作用,我有一些不明白的地方。您的意思是验证日期必须有效是无效的,还是需要日期的事实无效?

Fluent 验证也能够为不显眼的验证发出代码,因此所需的约束应该可以正常工作。 日期有效的事实完全是另一回事。如果您将 FromDate 指定为 DateTime(您是否将其声明为 DateTime 或字符串?)日期正确性的验证由 Mvc 框架中包含的其他验证器自动执行,因此您不需要在流利的语言中重复它验证规则。但是,在 Mvc4 之前,此验证检查仅在服务器端执行。使用 Mvc 4,Asp.net Mvc 团队解决了这个问题,并在客户端扩展了检查。 但是,在客户端,一切仅适用于 en-US 日期格式,因为不显眼的验证不处理全球化。如果您需要其他文化中的约会,您需要使用Globalize library,并且您需要在客户端设置全球化。如果您对全球化感兴趣,您可以查看this post of my blog。 要将自动日期更正也添加到 Mvc 3。您必须定义一个扩展的 ClientDataTypeModelValidatorProvider,如 Mvc4 之一。代码下方:

public class ClientDataTypeModelValidatorProviderExt : ClientDataTypeModelValidatorProvider
{
    public static Type ErrorMessageResources { get; set; }
    public static string NumericErrorKey { get; set; }
    public static string DateTimeErrorKey { get; set; }
    private static readonly HashSet<Type> _numericTypes = new HashSet<Type>(new Type[] {
        typeof(byte), typeof(sbyte),
        typeof(short), typeof(ushort),
        typeof(int), typeof(uint),
        typeof(long), typeof(ulong),
        typeof(float), typeof(double), typeof(decimal)
    });
    private static bool IsNumericType(Type type)
    {
        Type underlyingType = Nullable.GetUnderlyingType(type); // strip off the Nullable<>
        return _numericTypes.Contains(underlyingType ?? type);
    }
    internal sealed class NumericModelValidator : ModelValidator
    {
        public NumericModelValidator(ModelMetadata metadata, ControllerContext controllerContext)
            : base(metadata, controllerContext)
        {
        }

        public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
        {
            ModelClientValidationRule rule = new ModelClientValidationRule()
            {
                ValidationType = "number",
                ErrorMessage = MakeErrorString(Metadata.GetDisplayName())
            };

            return new ModelClientValidationRule[] { rule };
        }

        private static string MakeErrorString(string displayName)
        {
            // use CurrentCulture since this message is intended for the site visitor
            return String.Format(CultureInfo.CurrentCulture, ErrorMessageResources.GetProperty(NumericErrorKey, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static).GetValue(null, null) as string, displayName);
        }

        public override IEnumerable<ModelValidationResult> Validate(object container)
        {
            // this is not a server-side validator
            return Enumerable.Empty<ModelValidationResult>();
        }
    }
    internal sealed class DateTimeModelValidator : ModelValidator
    {
        public DateTimeModelValidator(ModelMetadata metadata, ControllerContext controllerContext)
            : base(metadata, controllerContext)
        {
        }

        public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
        {
            ModelClientValidationRule rule = new ModelClientValidationRule()
            {
                ValidationType = "globalizeddate",
                ErrorMessage = MakeErrorString(Metadata.GetDisplayName())
            };

            return new ModelClientValidationRule[] { rule };
        }

        private static string MakeErrorString(string displayName)
        {
            // use CurrentCulture since this message is intended for the site visitor
            return String.Format(CultureInfo.CurrentCulture, ErrorMessageResources.GetProperty(DateTimeErrorKey, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static).GetValue(null, null) as string, displayName);
        }

        public override IEnumerable<ModelValidationResult> Validate(object container)
        {
            // this is not a server-side validator
            return Enumerable.Empty<ModelValidationResult>();
        }
    }
    public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)
    {

        if (metadata == null)
        {
            throw new ArgumentNullException("metadata");
        }
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }
        List<ModelValidator> res = null;
        if (NumericErrorKey == null || ErrorMessageResources == null)
            res = base.GetValidators(metadata, context).ToList();
        else
        {
            res = new List<ModelValidator>();
            if (IsNumericType(metadata.ModelType))
            {
                res.Add(new NumericModelValidator(metadata, context));
            }
        }
        if ( (metadata.ModelType == typeof(DateTime) || metadata.ModelType == typeof(DateTime?)))
        {
            if(ErrorMessageResources != null && DateTimeErrorKey != null)
                res.Add(new DateTimeModelValidator(metadata, context));
        }
        return res;
    }
}

然后在 global.asax 中你必须用这个替换标准的:

var old = ModelValidatorProviders.Providers.Where(x => x is ClientDataTypeModelValidatorProvider).FirstOrDefault();
        if (old != null) ModelValidatorProviders.Providers.Remove(old);
        ModelValidatorProviders.Providers.Add(new ClientDataTypeModelValidatorProviderExt());

现在您必须添加在客户端进行控制的 javascript 片段:

$.validator.addMethod(
"globalizeddate",
 function (value, element, param) {
    if ((!value || !value.length) && this.optional(element)) return true; /*success*/       
    var convertedValue  = Globalize.parseDate(value);
            return !isNaN(convertedValue) && convertedValue;
  },
  "field must be a date/time"

);

在那里,我使用了 Globalize 功能来验证正确的日期。安装它。这是使日期格式与 .net 格式兼容的唯一方法。此外,它适用于所有 .net 文化。使用标准 javascript 日期解析,与 .Net 在某些浏览器中接受的格式不兼容。

【讨论】:

  • 感谢您的回答。你能分享一下你对 mvc3 的经验吗?
  • 将客户端验证添加到流利验证的方法是实现一个验证器,因此您不能简单地使用.Must,因为它不会让位于添加验证。您的验证器实现 IClientValidatable 接口就足够了。 fluent 验证项目在这里解释了如何创建自定义验证器:fluentvalidation.codeplex.com/… 为了添加客户端验证,您的自定义验证器必须实现 IClientValidatable。
  • 实现IClientValidatable的方式和DataAnnotations完全一样。不幸的是,由于这个原因,流利的验证网站没有解释它。请参阅此处了解实现所有客户端验证内容的简单方法:stackoverflow.com/questions/5154231/… 它用于 DataAnnotations,正如我所说的那样,它是相同的。在 .Net 方面,您的验证器必须实现相同的 IClientValidatable 类,而在 javascript 方面的内容完全相同。
  • 这表示数据有效性的检查不应该通过应用于每个 DateTime 的规则来实现,而应该像 Mvc4 那样自动应用于所有 DateTime。做到这一点的方法是将 Mvc 3 的 ClientDataTypeModelValidatorProvider 替换为仅表示格式错误的数字的 ClientDataTypeModelValidatorProvider ,用一个也验证日期的方法(如 Mvc 4 的那个)。我有解决问题的代码,但不是那么独立,不能在这里插入,如果您有兴趣,我可以为您提供该代码。
  • 什么的示例代码?一个 ClientDataTypeModelValidatorProvider 能够自动发出您在示例中需要的所有 Hill 格式的日期,或者如何实现 IClientValidatable 接口 + 相关的 javascript 内容?
【解决方案3】:

在 MVC 3 中,下面的代码应该可以正常工作。

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

@using (Html.BeginForm("Index", "Person", FormMethod.Post))
{   
    @Html.LabelFor(x => x.FromDate)
    @Html.EditorFor(x => x.FromDate)
    @Html.ValidationMessageFor(x => x.FromDate)

    <input type="submit" name="Submit" value="Submit" />
}

MVC 4 中的简单工作示例

_Layout.cshtml:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>@ViewBag.Title - My ASP.NET MVC Application</title>
        <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
        <meta name="viewport" content="width=device-width" />


        @Styles.Render("~/Content/css")
        @Scripts.Render("~/bundles/modernizr")
    </head>
    <body>

        <div id="body">
            @RenderSection("featured", required: false)
            <section class="content-wrapper main-content clear-fix">
                @RenderBody()
            </section>
        </div>

        @Scripts.Render("~/bundles/jquery")
        @RenderSection("scripts", required: false)


    </body>
</html>

查看:

@model Mvc4Test.Models.Person

@{
    ViewBag.Title = "test";

}

<h2>test</h2>

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>Part</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Name)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Name)
            @Html.ValidationMessageFor(model => model.Name)
        </div>



        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

For more details.

【讨论】:

  • 这称为数据注释而不是流利验证。对吧?
猜你喜欢
  • 1970-01-01
  • 2020-02-17
  • 2014-03-27
  • 2023-04-01
  • 2015-12-04
  • 1970-01-01
  • 1970-01-01
  • 2017-05-21
  • 1970-01-01
相关资源
最近更新 更多