65.9K
CodeProject 正在变化。 阅读更多。
Home

自定义 MVC 验证 – 快速入门

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.60/5 (6投票s)

2015 年 9 月 27 日

CPOL

7分钟阅读

viewsIcon

27925

downloadIcon

442

ASP.NET MVC 项目中实现客户端和服务器端验证的多种方法的快速入门指南。

引言

本文旨在帮助开发人员在基于 ASP.NET MVC 的项目中快速设置客户端和服务器端验证。已涵盖使用 Jquery Validation Plugin 和 MVC DataAnnotation 进行验证的技巧。代码片段易于编辑。

为客户端验证设置项目

包含客户端验证库脚本

为了快速开始客户端验证,我们可以利用基于 jquery 的验证库。如果您的项目 Scripts 文件夹中没有 jquery.validate.min.jsjquery.validate.unobtrusive.min.js,请通过“项目”菜单中的 Nuget 管理器或“工具”菜单中的程序包管理器控制台安装这两个库的程序包。在 Nudget 管理器中,您可以搜索并安装 “jquery.Validation”“jquery.Validation.Unobtrusive” 程序包。

添加脚本文件引用

要使用我们通过 Nuget 程序包管理器安装的库,请使用 <script> 标签包含 jquery.validate.min.jsjquery.validate.unobtrusive.min.js 脚本文件,如下所示,或者更好的是,使用捆绑!在 _Layout.cshtml 页面的 </body> 标签之前,或者任何您想实现客户端验证的地方,添加以下标签。由于它们依赖于 JQuery,因此您需要在包含这两个脚本文件之前包含 JQuery 脚本文件。

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

启用客户端验证和不可思议的 JavaScript

在您的 web.config 中,如果尚未存在,请包含以下应用设置键/值对。确保 value 属性设置为 “true”,否则可能无法按预期工作。

<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />

开发客户端和服务器端验证逻辑

客户端验证(通过在插件的 addMethod() 中实现逻辑)

创建验证规则

创建一个名为 “NonNumeric” 的 JQuery 验证规则。此验证规则将确保应用它的文本字段不包含数字数据。在 <script> 标签内,或在一个单独的“.js”文件中编写以下 JavaScript 代码,并将脚本文件包含在页面中。

jQuery.validator.addMethod("nonnumeric",
    function (value, element, params) {
        $.validator.messages.nonnumeric = 
            jQuery.validator.format("This field cannot contain numeric characters.")
        return value.match(/\d+/g) == null;
    },
    $.validator.messages.nonnumeric
);

实现验证规则

在自定义 JavaScript 验证代码准备好之后,将其作为规则添加到需要它的字段。在此示例中,我们将 NonNumeric 验证规则应用于字段 “txtPlanetName”。您可以在页面的文档就绪函数中编写此代码。 

$("#txtPlanetName").rules("add",
{
    nonnumeric: true,
    messages: {
        nonnumeric: "My Error Message: Planet Name cannot contain numeric characters."
    }
});

客户端验证(通过在通用 Javascript 方法中实现逻辑)

创建一个名为 “TriggerError” 的 Jquery 验证规则。与我们之前的示例不同,此验证规则不包含自己的验证逻辑。添加此规则将自动触发验证错误以及现有的验证失败列表。因此,此验证规则需要应用于具有无效数据的字段。当需要使用现有的验证逻辑并将其与基于 JQuery 插件的其他验证集成时,此技术可能会很有用。

创建验证规则

创建一个名为 “TriggerError” 的 Jquery 验证规则。每当将此验证规则添加到字段时,该字段就被视为具有无效数据。在下面的代码中,您可以看到此规则始终返回 false。这意味着当 JQuery 验证器尝试执行此验证规则时,它每次都会失败。这在您想使用现有验证逻辑的地方非常有用。然后,您可以在使用 @Html.ValidationSummary()@Html.ValidationFor() HTML 辅助器出现的通用验证错误列表中添加验证错误。在 <script> 标签内,或在一个单独的“.js”文件中编写以下 JavaScript 代码,并将脚本文件包含在页面中。

jQuery.validator.addMethod("triggererror",
    function (value, element, params) {
        $.validator.messages.triggererror = 
            jQuery.validator.format("This field contains invalid data.");
        return false;
    },
    $.validator.messages.triggererror
);

实现验证规则

在自定义 JavaScript 验证代码准备好之后,您可以将其作为规则添加到需要它的字段。在此示例中,我们将 TriggerError 验证规则应用于字段 “txtNumberOfMoons”。您可以在页面的文档就绪函数中编写此代码。 

if (parseInt($("#txtNumberOfMoons").val()) < 0) {
    $("#txtNumberOfMoons").rules("add",
    {
        triggererror: true,
        messages: {
            triggererror: "My Error Message: Numbers of Moons " + 
                "field should have a value greater than or equal to 0."
        }
    });
}

服务器端验证

我们将创建一个名为 “RequiredIfChecked”ValidationAttribute 。此验证可确保在相关复选框被选中时文本字段包含数据。例如,如果您已婚,则可能希望用户提供纪念日日期。

创建验证属性

创建一个名为 “RequiredIfChecked” 的 C# 验证属性规则。每当将此自定义验证属性应用于类属性时,都会执行关联的验证逻辑。为此,我们需要创建一个继承自 ValidationAttribute 类并实现 IsValid() 方法的类。这是您将编写验证逻辑的方法。您可以将以下代码写入一个新的类文件 RequiredIfChecked.cs。请注意,Utility 类是一个自定义类,可以重用它来快速获取常用代码行的值。Utility 类源代码如下,并在本次撰写文章的源代码下载中提供。

public class RequiredIfChecked : ValidationAttribute
{
    private const string _DefaultErrorMessage = "{0} field is required when {1} field is selected.";

    private string _DependentPropertyName;

    public RequiredIfChecked(string dependentPropertyName)
    {
        _DependentPropertyName = dependentPropertyName;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        bool isChecked = false;
        //Get details about the dependent property
        PropertyInfo dependentProperty = 
            Utility.GetPropertyInfo(validationContext, _DependentPropertyName);
        string dependentPropertyValue = 
            Utility.GetPropertyValueAsString(validationContext, _DependentPropertyName);

        if (bool.TryParse(Convert.ToString(dependentPropertyValue), out isChecked))
        {
            //Validation should fail if the checkbox is selected but the main field is empty
            if (isChecked
                    && string.IsNullOrEmpty(Convert.ToString(value)))
            {
                return new ValidationResult(Utility.GetValidationErrorMessage(this,
                                                        _DefaultErrorMessage,
                                                        Utility.GetDisplayName(validationContext),
                                                        Utility.GetDisplayName(dependentProperty)));
            }
        }
        return ValidationResult.Success;
    }
}

实现验证属性

实现自定义验证属性非常直接。如上例代码所示,我们将 RequiredIfChecked 验证属性应用于我们的字段 AtmosphereNotes。此验证可确保,如果选中 “IsHabitable” 复选框(值为 true),则 AtmoshpereNotes 字段不能为空。在 MVC 中,您可以将验证属性应用于您的 Model 或 ViewModel 类属性,如下面的代码所示。 

[Display(Name = "Is Habitable")]
public bool IsHabitable { get; set; }

[RequiredIfChecked("IsHabitable",ErrorMessage = "My Error Message: {0} field is required when {1} field is selected.")]
[DataType(DataType.MultilineText)]
[Display(Name = "Atmosphere Notes")]
public string AtmosphereNotes { get; set; }

客户端和服务器端验证

我们将创建一个名为 “RequiredIfNotEmpty” 的验证属性。此验证可确保当相关文本框有数据时,文本字段也包含数据。例如,当提供了地址时,您可能希望用户提供邮政编码。

创建服务器端验证属性

我们将创建一个名为 “RequiredIfNotEmpty” 的 C# 验证属性规则。每当将此自定义验证属性应用于类属性时,都会执行关联的验证逻辑。为此,我们需要创建一个继承自 ValidationAttribute 类并实现 IsValid() 方法的类。这是您将编写验证逻辑的方法。您可以将以下代码写入一个新的类文件 RequiredIfNotEmpty.cs。与我们之前的示例一样,请注意,Utility 类是一个自定义类,可以重用它来快速获取常用代码行的值。Utility 类源代码如下,并在本次撰写文章的源代码下载中提供。

public class RequiredIfNotEmpty : ValidationAttribute
{
    private const string _DefaultErrorMessage = "{0} field is required when {1} field is not empty.";

    private string _DependentPropertyName;

    public RequiredIfNotEmpty(string dependentPropertyName)
    {
        _DependentPropertyName = dependentPropertyName;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (string.IsNullOrEmpty(Convert.ToString(value)))
        {
            //Get details about the dependent property
            PropertyInfo dependentProperty = 
                Utility.GetPropertyInfo(validationContext, _DependentPropertyName);
            string dependentPropertyValue = 
                Utility.GetPropertyValueAsString(validationContext, _DependentPropertyName);

            //Validation should fail if dependent field has data but the main field is empty
            if (!string.IsNullOrEmpty(dependentPropertyValue)
                    && string.IsNullOrEmpty(Convert.ToString(value)))
            {
              return new ValidationResult(Utility.GetValidationErrorMessage(this,
                                                        _DefaultErrorMessage,
                                                        Utility.GetDisplayName(validationContext),
                                                        Utility.GetDisplayName(dependentProperty)));
            }
        }
        return ValidationResult.Success;
    }
}

启用和链接客户端验证属性

我们需要做一些额外的工作来启用客户端验证并将其链接到服务器端验证。理解这两段代码是独立的,并且您需要确保使用 JavaScript 编写的逻辑与 C# 中为服务器端验证编写的逻辑相似,这一点很重要。为此,请首先在 RequiredIfNotEmpty 类中实现 IClientValidatable 接口。接下来,在 RequiredIfNotEmpty 类中包含以下代码。

public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
    ModelClientValidationRule rule = new ModelClientValidationRule();
    rule.ValidationType = "requiredifnotempty";
    rule.ErrorMessage = Utility.GetValidationErrorMessage(this,
                       _DefaultErrorMessage,
                       metadata.GetDisplayName(),
                       Utility.GetDisplayName(metadata, context,_DependentPropertyName)); 
    rule.ValidationParameters.Add("param", _DependentPropertyName);
    yield return rule;
}

创建客户端验证规则 

现在编写以下 JavaScript 代码。适配器为您的 HTML 控件提供了额外的 data 属性。您可以在需要时将这些属性用于 JavaScript 验证逻辑。在我们的例子中,param 属性值代表相关字段的名称,用于确定它是否为空。

jQuery.validator.unobtrusive.adapters.add("requiredifnotempty", ["param"],
    function (options) {
        options.rules["requiredifnotempty"] = options.params.param;
        options.messages["requiredifnotempty"] = options.message;
    }
);

jQuery.validator.addMethod("requiredifnotempty",
    function (value, element, param) {
        var isValid = true;
        if ($("[name='" + param + "']").val().trim().length != 0
            && $.trim(value).length == 0) {
            isValid = false;
        }
        return isValid;
    }
);

实现验证属性

在下面的示例代码中,我们将 RequiredIfNotEmpty 验证属性应用于 OrbitingStarName StarAge 属性。这确保了当任一字段有值时,两个字段都需要有值。在 MVC 中,您可以将验证属性应用于 Model 或 ViewModel 类属性,如下面的代码所示。 

[RequiredIfNotEmpty("StarAge", 
    ErrorMessage = "My Error Message: {0} field is required when {1} field is not empty.")]
[Display(Name = "Orbiting Star Name")]
public string OrbitingStarName { get; set; }

[RequiredIfNotEmpty("OrbitingStarName", 
    ErrorMessage = "My Error Message: {0} field is required when {1} field is not empty.")]
[Display(Name = "Star Age")]
public string StarAge { get; set; }

在服务器上检查验证错误

当用户通过单击提交按钮提交表单时,程序控制流到控制器操作。在操作方法中,您可以检查 Model 是否具有有效数据。为此,我们可以使用 ModelState 对象,如下所示。

[HttpPost]
public ActionResult TestValidations(Planet viewModel)
{
    if(ModelState.IsValid)
    {
        //Save the provided data
    }
    else
    {
        //Tell the user to provide correct data
    }
    return View(viewModel);
}

在客户端检查验证错误

当用户在表单标签内单击“提交”按钮时,JQuery 验证会自动触发。如果您像我们在上面示例中展示的那样包含了自定义验证规则,例如 TriggerError ,则在表单提交之前,您需要触发验证错误。为此,在您的视图(.cshtml 页面)中,将 HTML 按钮类型从 “submit” 改为 “button”,并使用下面的代码。

function btnSubmit_Click() {
    $('#frmAddPlanet').validate();
           
    if (parseInt($("#txtNumberOfMoons").val()) < 0) {
        $("#txtNumberOfMoons").rules("add",
        {
            triggererror: true,
            messages: {
                triggererror: "My Error Message: Numbers of Moons field " + "
                    should have a value greater than or equal to 0."
            }
        });
    }

    if ($("#frmAddPlanet").valid()) {
        $("#frmAddPlanet").submit();
    }
}

实用类源代码

public class Utility
{
    public static PropertyInfo GetPropertyInfo(ValidationContext validationContext, string propertyName)
    {
        return validationContext.ObjectInstance.GetType().GetProperty(propertyName);
    }

    public static string GetPropertyValueAsString(ValidationContext validationContext, 
        string propertyName)
    {
        return Convert.ToString(GetPropertyInfo(validationContext, propertyName).GetValue(validationContext.ObjectInstance, null));
    }

    public static string GetDisplayName(PropertyInfo propertyInfo)
    {
        string fieldDisplayName = propertyInfo.Name;

        if (propertyInfo.GetCustomAttributes(typeof(DisplayAttribute), true).Count() > 0)
        {
            fieldDisplayName = propertyInfo.GetCustomAttributes(typeof(DisplayAttribute), true).Cast<DisplayAttribute>().Single().Name;
        }

        return fieldDisplayName;
    }

    public static string GetDisplayName(ValidationContext validationContext)
    {
        return validationContext.DisplayName;
    }

    public static string GetDisplayName(ModelMetadata modelMetadata)
    {
        return modelMetadata.GetDisplayName();
    }

    public static string GetDisplayName(ModelMetadata modelMetadata, 
        ControllerContext context, string propertyName)
    {
        var parentMetaData = ModelMetadataProviders.Current.GetMetadataForProperties(context.Controller.ViewData.Model, modelMetadata.ContainerType);
        return parentMetaData.Single(x => x.PropertyName == propertyName).GetDisplayName();
    }

    public static string GetValidationErrorMessage(ValidationAttribute validationAttribute, 
        string defaultErrMsg, params object[] args)
    {
        string valErrMsg = string.Empty;
        if (!string.IsNullOrEmpty(validationAttribute.ErrorMessage))
            valErrMsg = validationAttribute.ErrorMessage;
        else
            valErrMsg = defaultErrMsg;

        valErrMsg = string.Format(valErrMsg, args);

        return valErrMsg;
    }
}

 

历史

  • 2015 年 9 月 27 日:初始版本
© . All rights reserved.