Asp.NET MVC:验证年龄> 18有3个组合框

时间:2014-10-13 14:56:29

标签: c# asp.net-mvc

我在3组合框中显示了MVC中的出生日期,月份和年份。我想计算年龄并且不允许注册年龄小于18岁的男性。虽然JS。

与此图片中显示的内容类似: enter image description here

这是通过使用DataAnnotations和EditorFor完成的。实际的源代码类似于以下内容。我应该如何修改以共同验证3个控件?

[Required(ErrorMessageResourceType = typeof (Resources), 
          ErrorMessageResourceName = "RequiredField")]
[Range(1, 31)]
[LocalizedDisplayName(typeof (RA.Resources), "RegistrationDayOfBirth")]
public int BirthDay { get; set; }

[Required(ErrorMessageResourceType = typeof (Resources), 
          ErrorMessageResourceName = "RequiredField")]
[Range(1, 12)]
[LocalizedDisplayName(typeof (RA.Resources), "RegistrationMonthOfBirth")]
public int BirthMonth { get; set; }

[Required(ErrorMessageResourceType = typeof (Resources), 
          ErrorMessageResourceName = "RequiredField")]
[Range(1800, int.MaxValue, ErrorMessageResourceType = typeof (Resources),
       ErrorMessageResourceName = "MoreThanFieldRequired")]
[LocalizedDisplayName(typeof (RA.Resources), "RegistrationYearOfBirth")]
public int BirthYear { get; set; }

[LocalizedDisplayName(typeof (RA.Resources), "RegistrationDateOfBirth")]
public DateTime DateOfBirth { get; set; }

2 个答案:

答案 0 :(得分:4)

如果你想坚持使用3场方法,并且要进行动态验证(即如果明天我18岁生日就拒绝让我访问,但明天就让我明白),你需要有创意

然后,您需要创建一个自定义验证器,以及一些自定义属性。

你如何做到这一点取决于你想要做的工作量,以及你想要应用验证逻辑的位置。

仅限服务器端验证

最简单的选择是在类本身上定义它 - 但是这将仅限于服务器端验证。

创建一个在类级别应用的自定义属性,期望该类上有三个字段(我已经添加了一个接口以使这更简单并且不需要反射)并根据需要对其进行验证:

// Interface to define individual fields:
public interface IHasIndividualDateOfBirth
{
  int BirthDay { get; set; }
  int BirthMonth { get; set; }
  int BirthYear { get; set; }
}

// Note new class level attribute, and interface declaration:
[MinAge(AgeInYears = 18)]
public class Birthday: IHasIndividualDateOfBirth
{
  [Required]
  [Range(1, 31)]
  public int BirthDay { get; set; }
  [Required]
  [Range(1, 12)]
  public int BirthMonth { get; set; }
  [Required]
  [Range(1800, 2200)]
  public int BirthYear { get; set; }

  public DateTime BirthDate { get; set; }
}

// Declare a new ValidationAttribute that can be used at the class level:
[AttributeUsage(AttributeTargets.Class)]
public class MinAgeAttribute : ValidationAttribute
{
  public int AgeInYears { get; set; }

  // Implement IsValid method:
  protected override ValidationResult IsValid(object value, 
                                              ValidationContext validationContext)
  {
    // Retrieve the object that was passed in as our DateOfBirth type
    var objectWithDob = validationContext.ObjectInstance 
                          as IHasIndividualDateOfBirth;

    if (null != objectWithDob)
    {
      // TODO: Handle invalid dates from the front-end (30 Feb for example)
      DateTime dateOfBirth = new DateTime(objectWithDob.BirthYear, 
                                          objectWithDob.BirthMonth, 
                                          objectWithDob.BirthDay);

      // Check that the age is more than the minimum requested
      if (DateTime.Now >= dateOfBirth.AddYears(AgeInYears))
      {
        return ValidationResult.Success;
      }

      return new ValidationResult("You are not yet 18 years old");
    }

    return new ValidationResult("Class doesn't implement IHasIndividualBirthday");
  }
}

虽然实施IValidatableObject似乎更简单,但它不像使用属性那样灵活,而且(如上面基于类的验证)也没有提供执行客户端的方法 - 方验证。

其他选项是创建一个依赖于许多其他字段的验证器(在这种情况下,您可能需要使用反射来查找其他字段,并确定哪些字段在哪里)并且您可以需要确保您只触发验证器一次(而不是在每个字段上),或者为DateTime属性编写自定义验证器和编辑器,而不是渲染单个字段,您可以删除日历控件以创建你之后的三个独立领域。

客户端和服务器端验证

要使客户端验证正常工作,您需要在属性级别执行此操作,这将需要您执行一些额外的工作 - 例如,您可以使用模型上的DateTime字段作为当用户填写各个字段然后验证时,通过JS填充的隐藏字段。

您的属性需要实现IClientValidatable,这将使您能够挂钩到客户端验证选项,并在元素上呈现一些元数据以公开年龄要求:

[AttributeUsage(AttributeTargets.Property)]
public class MinAgeAttribute : ValidationAttribute, IClientValidatable
{
  public int AgeInYears { get; set; }

  protected override ValidationResult IsValid(object value,
                                              ValidationContext validationContext)
  {
    // [Similar to before]
  }

  public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
                                                        ModelMetadata metadata,
                                                        ControllerContext context)
  {
    return new[]
    {
      new ModelClientValidationMinAgeRule(ErrorMessage, AgeInYears)
    };
  }
}

public class ModelClientValidationMinAgeRule : ModelClientValidationRule
{
  public ModelClientValidationMinAgeRule(string errorMessage, int ageInYears)
  {
      ErrorMessage = errorMessage;
      // Validation Type and Parameters must be lowercase
      ValidationType = "minage";
      ValidationParameters.Add("ageinyears", ageInYears);
  }
}

然后对于客户端,您需要将一些自定义验证器注册到jQuery.Validate或类似的(我推荐您自己的jqueryval包中包含的JS文件):

$(function ($) {
  $.validator.addMethod("minage", function(value, element, params) {
    if ($(element).val() != '') {

      var ageInYears = params;

      // take date from BirthDate element and compare with ageInYears.
      return false;
    }
  });

  $.validator.unobtrusive.adapters.addSingleVal("minage", "ageinyears");
}(jQuery));

答案 1 :(得分:1)

Zhaph的回答很好,但是一旦你实现了他的解决方案,它就忽略了如何从客户端的角度来看待这个问题。基本上,他将验证从三个单独的字段切换到一个新字段BirthDate。因此,您需要在页面上包含此属性的验证结果,而不是三个单独的字段。通常,您会执行以下操作:

<label for="@Html.IdFor(m => m.BirthMonth)">
    @Html.DisplayNameFor(m => m.BirthDate)
</label>
<div>
    @Html.EditorFor(m => m.BirthMonth)
    @Html.EditorFor(m => m.BirthDay)
    @Html.EditorFor(m => m.BirthYear)
</div>
@Html.ValidationMessageFor(m => m.BirthDate)

关键点:

  1. 标签用于组的第一个字段,因此单击标签将使用户按原样进入该字段。但是,标签的实际值来自BirthDate字段,因此它代表整个字段组,而不仅仅是第一个字段,在本例中为BirthMonth

  2. 验证消息也来自BirthDate字段,根据Zhaph的实现,它会打印出“你还不到18岁”的字样,只需一次。

  3. 要在客户端实现控件,您可以使用JQuery插件,例如 jQuery Validation Plugin ,并查看以下文章:ASP.NET MVC Client Side Validation