asp.net-mvc asp.net-mvc-3 fluentvalidation

asp.net mvc - ¿Cómo validar la fecha en ClientSide usando FluentValidation?



asp.net-mvc asp.net-mvc-3 (3)

En MVC 3 a continuación el código debería funcionar bien.

<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" /> }

Ejemplo de trabajo simple en 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>

Ver:

@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") }

Para más detalles.

Pregunta

El código siguiente funciona bien desde el lado del Server y no desde el lado del cliente. Por qué ?


Cuando envío el formulario, el control pasa a la función BeAValidDate para verificar que la fecha sea válida o no. ¿Hay alguna manera de Validate la fecha sin ir al servidor mediante Fluent Validation ?

Guiones

<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>

Modelo

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); } }

Controlador

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); } }

Ver

@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" /> }


Hay cosas que no entiendo sobre su configuración y sobre lo que no funciona. ¿Quiere decir que la verificación de que la fecha debe ser válida no funciona, o que el hecho de que la fecha sea necesaria no funciona?

La validación fluida también puede emitir código para una validación discreta, por lo que la restricción requerida debería funcionar correctamente. El hecho de que la fecha sea válida es completamente otra historia. Si especifica FromDate como DateTime (¿lo ha declarado como DateTime o como cadena?) La verificación de la corrección de la fecha se realiza AUTOMÁTICAMENTE por otros validadores incluidos en el marco de Mvc, por lo que no necesita repetirlo en la fluidez reglas de validación Sin embargo, antes de Mvc4 esta verificación de validación se realizó SOLAMENTE en el lado del servidor. Con Mvc 4, el equipo Asp.net Mvc solucionó este problema y extendió el cheque también en el lado del cliente. Sin embargo, en el lado del cliente, todo funciona correctamente solo para el formato de fecha en-US, ya que la validación discreta no maneja la globalización. Si necesita una fecha en otras culturas, necesita usar la biblioteca Globalize , y debe configurar la globalización en el lado del cliente. Si le interesa la globalización, puede ver esta publicación de mi blog . Para agregar la corrección de fecha automática también a Mvc 3. Debe definir un ClientDataTypeModelValidatorProvider extendido como el de Mvc4. Debajo del código:

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; } }

Luego, en global.asax debes sustituir el estándar con esto:

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

Ahora tiene que agregar el javascript cortado que hace el control en el lado del cliente:

$.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"

);

Ahí utilicé la función Globalize para verificar la fecha correcta. Instalarlo. Es la única forma de tener un frormat de fecha compatible con el formato .net. Además, funciona en todas las culturas .net. El uso de un análisis de fecha de JavaScript estándar no es compatible con los formatos aceptados por .Net en algunos navegadores.


Truco usando Greater Then Or Equal To Validator. Funciona para mi.

Global.asax - Evento de inicio de aplicación

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

Modelo

[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; } }

Regla

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; } }

Método de acción del controlador

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); }

Ver

@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> }

Guión

<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>