ASP.NET MVC客户端验证

7

在阅读了ScottGU的博客文章之后,我一直在调整ASP.net MVC中的客户端验证功能。使用System.ComponentModel.DataAnnotations属性非常容易,就像这样:

    [Required(ErrorMessage = "You must specify a reason")]
    public string ReasonText { get; set; }

...但是如果您需要更复杂的内容怎么办?如果您有一个包含邮政编码和国家代码字段的地址类。您希望针对每个国家/地区验证邮政编码与不同的正则表达式进行比较,例如[0-9]{5}适用于美国,但加拿大需要不同的验证规则。

为了解决这个问题,我创建了自己的ValidationService类,该类使用控制器的ModelState属性并相应地进行验证。这在服务器端很好地工作,但无法使用新的客户端验证。

在Webforms中,我会使用类似RequiredFieldValidator或CompareValidator的javascript-emitting控件来处理简单的内容,然后使用CustomValidator处理复杂规则。这样一来,我将所有的验证逻辑都放在了一个地方,并且可以获得快速的javascript验证(90% 的情况下)以及作为备份的安全的服务器端验证。

在MVC中,应该如何实现这种等效方法呢?


我不确定,但我认为你必须自己编写客户端验证程序来处理这样的自定义内容。也许可以看一下Jquery Validation- http://docs.jquery.com/Plugins/validation - Vishal
你的目标是哪个版本的ASP.NET MVC? - Nathan Taylor
5个回答

7

编辑:此假设您正在使用MVC 3。不幸的是,我的代码是VB.NET,因为这是我在工作中必须使用的。

为了使所有内容与新的非侵入式验证完美配合,您需要执行一些操作。几周前,我已经完成了这些操作。

首先,创建一个自定义属性类,该类继承自ValidationAttribute。下面是一个简单的RequiredIf属性类:

Imports System.ComponentModel
Imports System.ComponentModel.DataAnnotations

<AttributeUsage(AttributeTargets.Field Or AttributeTargets.Property, AllowMultiple:=False, Inherited:=False)> _
Public NotInheritable Class RequiredIfAttribute
    Inherits ValidationAttribute

    Private Const    _defaultErrorMessage As String = "'{0}' is required."
    Private ReadOnly _dependentProperty   As String
    Private ReadOnly _targetValues        As Object()

    Public Sub New(dependentProperty As String, targetValues As Object())

        MyBase.New(_defaultErrorMessage)

        _dependentProperty = dependentProperty
        _targetValues      = targetValues

    End Sub

    Public Sub New(dependentProperty As String, targetValues As Object(), errorMessage As String)

        MyBase.New(errorMessage)

        _dependentProperty = dependentProperty
        _targetValues      = targetValues

    End Sub

    Public ReadOnly Property DependentProperty() As String
        Get
            Return _dependentProperty
        End Get
    End Property

    Public ReadOnly Property TargetValues() As Object()
        Get
            Return _targetValues
        End Get
    End Property

    Public Overrides Function FormatErrorMessage(name As String) As String

        Return String.Format(Globalization.CultureInfo.CurrentUICulture, ErrorMessageString, name)

    End Function

    Protected Overrides Function IsValid(value As Object, context As ValidationContext) As ValidationResult

        ' find the other property we need to compare with using reflection
        Dim propertyValue = context.ObjectType.GetProperty(DependentProperty).GetValue(context.ObjectInstance, Nothing).ToString()

        Dim match = TargetValues.SingleOrDefault(Function(t) t.ToString().ToLower() = propertyValue.ToLower())

        If match IsNot Nothing AndAlso value Is Nothing Then
            Return New ValidationResult(FormatErrorMessage(context.DisplayName))
        End If

        Return Nothing

    End Function

End Class

接下来,您需要实现一个验证器类。该类负责让MVC知道客户端验证规则,这些规则是必需的,以便无侵入式验证库能够正常工作。

Public Class RequiredIfValidator
    Inherits DataAnnotationsModelValidator(Of RequiredIfAttribute)

    Public Sub New(metaData As ModelMetadata, context As ControllerContext, attribute As RequiredIfAttribute)

        MyBase.New(metaData, context, attribute)

    End Sub

    Public Overrides Function GetClientValidationRules() As IEnumerable(Of ModelClientValidationRule)

        Dim rule As New ModelClientValidationRule() With {.ErrorMessage = ErrorMessage,
                                                          .ValidationType = "requiredif"}

        rule.ValidationParameters("dependentproperty") = Attribute.DependentProperty.Replace("."c, HtmlHelper.IdAttributeDotReplacement)

        Dim first       As Boolean = True
        Dim arrayString As New StringBuilder()

        For Each param In Attribute.TargetValues
            If first Then
                first = False
            Else
                arrayString.Append(",")
            End If
            arrayString.Append(param.ToString())
        Next

        rule.ValidationParameters("targetvalues") = arrayString.ToString()

        Return New ModelClientValidationRule() {rule}

    End Function

End Class

现在您可以在Global.asax的应用程序启动方法中注册所有内容:
DataAnnotationsModelValidatorProvider.RegisterAdapter(GetType(RequiredIfAttribute), GetType(RequiredIfValidator))

这可以让你达到90%的效果。现在你只需要告诉JQuery validate和MS的不唐突验证层如何读取你的新属性:
/// <reference path="jquery-1.4.1-vsdoc.js" />
/// <reference path="jquery.validate-vsdoc.js" />

/* javascript for custom unobtrusive validation
   ==================================================== */

(function ($) {

    // this adds the custom "requiredif" validator to the jQuery validate plugin
    $.validator.addMethod('requiredif',
                          function (value, element, params) {

                              // the "value" variable must not be empty if the dependent value matches
                              // one of the target values
                              var dependentVal = $('#' + params['dependentProperty']).val().trim().toLowerCase();
                              var targetValues = params['targetValues'].split(',');

                              // loop through all target values
                              for (i = 0; i < targetValues.length; i++) {
                                  if (dependentVal == targetValues[i].toLowerCase()) {
                                      return $.trim(value).length > 0;
                                  }
                              }

                              return true;
                          },
                          'not used');

    // this tells the MS unobtrusive validation layer how to read the
    // HTML 5 attributes that are output for the custom "requiredif" validator
    $.validator.unobtrusive.adapters.add('requiredif', ['dependentProperty', 'targetValues'], function (options) {

        options.rules['requiredif'] = options.params;
        if (options.message) {
            options.messages['requiredif'] = options.message;
        }

    });

} (jQuery));

希望这能帮到你,这真是一件难事。

好的解释,从头到尾看起来很有道理。 - CRice
谢谢,@CRice。希望这能帮助其他人。当MVC 3还在测试阶段时,我曾经为像这样的东西而苦苦寻找,但只能找到一些零散的信息。 - Shea Daniels
@SheaDaniels:“不幸的是,我的代码是用VB.NET编写的,因为这是我在工作中必须使用的。”这已经足够找一份新工作了! - Elisabeth

3

ScottGu今天早上在推特上发布了消息,Pluralsight在接下来的48小时内提供免费的MVC 3培训。他们有一个视频展示如何进行自定义验证。相关视频位于“ASP.NET MVC 3.0中的模型”下,具体包括“自定义验证属性”和“自我验证模型”。


很棒的视频。而且通过免费赠送两天来进行精彩的营销。 - John Hoge
+1 很棒的视频。我刚刚注册了免费试用并观看了“自定义客户端验证”的视频。它非常详细,对我很有帮助。我现在正在进行为期10天的试用,可能会在试用期结束前再观看几个视频。他们提供了很好的服务。 - Aaron

1
ValidationAttribute派生您自己的验证属性,并相应地应用逻辑。在MVC 2中,为了根据另一个属性的值对属性执行验证,必须在验证器内部完成此操作,然后使用(假设您正在使用 DataAnnotationsModelValidatorProvider )注册要与自定义验证属性一起使用的验证器。
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(ValidationAttribute), typeof(ValidationValidator)); 

因为在验证属性中,您只能访问绑定到该属性的属性值,而无法访问模型。

请查看MVC FoolProof Validation,了解如何执行此方法。


那么这是否会自动在开箱即用的客户端验证中被捕获呢? - CRice
@CRice - 如果从System.ComponentModel.DataAnnotations中的某个属性派生,则客户端的处理将为您处理。如果定义自己的验证属性,那么您还需要自己编写客户端逻辑。假设您正在使用jQuery验证,则可以添加一个验证器函数来调用jQuery验证,由字符串键入以在验证规则匹配键时使用。 - Russ Cam
系统如何知道要输出哪个Javascript来用于验证任何自定义类,该类继承自System.ComponentModel.DataAnnotations.ValidationAttribute? - CRice
请查看http://foolproof.codeplex.com/SourceControl/changeset/view/67563#1188769以获取示例。 - Russ Cam
@CRice- 你的验证器已经注册到了你的验证属性上,并且拥有一个名为GetClientValidationRules的方法。在这个方法中,你需要给返回的ModelClientValidationRule类型的ValidationType属性赋一个字符串值,用来指示客户端使用的规则名称。 - Russ Cam
在MVC2中,当您调用“Html.EnableClientValidation()”之后,会写入一个脚本块,其中包含与客户端验证相关的数据,例如要使用的规则、要显示的错误消息等等。在启用了不显眼JavaScript选项的MVC 3中,这些数据位于表单字段元素上的HTML 5“data-”属性中。 - Russ Cam

1

我刚刚看到了MVC 3中关于IValidatableObject接口的一些内容,我会尝试一下。


0

我认为你的问题的解决方案是在MVC中使用System.ComponentModel.DataAnnotations

对于复杂的逻辑,你可以实现自己的逻辑。这很容易。 请查看此链接上的自定义内容: http://msdn.microsoft.com/en-us/library/cc668224.aspx

对于基本的事情,让你的视图与模型类绑定,并在属性顶部添加属性... 像这样

public class CustomerSearchDE
{
    [StringLength(2, ErrorMessageResourceType = typeof(Translation), ErrorMessageResourceName = MessageConstants.conCompanyNumberMaxLength)]
    public string CompanyNumber { get; set; }
 }

使用该类进行强类型视图

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接