asp.net-mvc - MVC 动态值的不引人注目的范围验证

标签 asp.net-mvc asp.net-mvc-3 data-annotations unobtrusive-validation

我的模型上有一个值,该值必须落在我的模型上的其他两个值的范围内。

例如:

public class RangeValidationSampleModel
{
    int Value { get; set; }

    int MinValue { get; set; }

    int MaxValue { get; set; }
}

当然,我无法将这些 Min/MaxValues 传递到我的 DataAnnotations 属性中,因为它们必须是常量值。

我确定我需要构建自己的验证属性,但我还没有做这么多,无法理解它应该如何工作。

我搜索了大约一个小时,并看到了各种用于构建自定义验证的解决方案,但找不到任何使用 MVC3 非侵入式验证来解决此特定问题的内容。

最佳答案

您可以为此目的编写自定义验证属性:

public class DynamicRangeValidator : ValidationAttribute, IClientValidatable
{
    private readonly string _minPropertyName;
    private readonly string _maxPropertyName;
    public DynamicRangeValidator(string minPropertyName, string maxPropertyName)
    {
        _minPropertyName = minPropertyName;
        _maxPropertyName = maxPropertyName;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var minProperty = validationContext.ObjectType.GetProperty(_minPropertyName);
        var maxProperty = validationContext.ObjectType.GetProperty(_maxPropertyName);
        if (minProperty == null)
        {
            return new ValidationResult(string.Format("Unknown property {0}", _minPropertyName));
        }
        if (maxProperty == null)
        {
            return new ValidationResult(string.Format("Unknown property {0}", _maxPropertyName));
        }

        int minValue = (int)minProperty.GetValue(validationContext.ObjectInstance, null);
        int maxValue = (int)maxProperty.GetValue(validationContext.ObjectInstance, null);
        int currentValue = (int)value;
        if (currentValue <= minValue || currentValue >= maxValue)
        {
            return new ValidationResult(
                string.Format(
                    ErrorMessage, 
                    minValue,
                    maxValue
                )
            );
        }

        return null;
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var rule = new ModelClientValidationRule
        {
            ValidationType = "dynamicrange",
            ErrorMessage = this.ErrorMessage,
        };
        rule.ValidationParameters["minvalueproperty"] = _minPropertyName;
        rule.ValidationParameters["maxvalueproperty"] = _maxPropertyName;
        yield return rule;
    }
}

然后用它装饰你的 View 模型:

public class RangeValidationSampleModel
{
    [DynamicRangeValidator("MinValue", "MaxValue", ErrorMessage = "Value must be between {0} and {1}")]
    public int Value { get; set; }
    public int MinValue { get; set; }
    public int MaxValue { get; set; }
}

那么你可以有一个 Controller 来提供 View :

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View(new RangeValidationSampleModel
        {
            Value = 5,
            MinValue = 6,
            MaxValue = 8
        });
    }

    [HttpPost]
    public ActionResult Index(RangeValidationSampleModel model)
    {
        return View(model);
    }
}

当然还有 View :

@model RangeValidationSampleModel

<script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script type="text/javascript">
    $.validator.unobtrusive.adapters.add('dynamicrange', ['minvalueproperty', 'maxvalueproperty'],
        function (options) {
            options.rules['dynamicrange'] = options.params;
            if (options.message != null) {
                $.validator.messages.dynamicrange = options.message;
            }
        }
    );

    $.validator.addMethod('dynamicrange', function (value, element, params) {
        var minValue = parseInt($('input[name="' + params.minvalueproperty + '"]').val(), 10);
        var maxValue = parseInt($('input[name="' + params.maxvalueproperty + '"]').val(), 10);
        var currentValue = parseInt(value, 10);
        if (isNaN(minValue) || isNaN(maxValue) || isNaN(currentValue) || minValue >= currentValue || currentValue >= maxValue) {
            var message = $(element).attr('data-val-dynamicrange');
            $.validator.messages.dynamicrange = $.validator.format(message, minValue, maxValue);
            return false;
        }
        return true;
    }, '');
</script>

@using (Html.BeginForm())
{
    <div>
        @Html.LabelFor(x => x.Value)
        @Html.EditorFor(x => x.Value)
        @Html.ValidationMessageFor(x => x.Value)
    </div>
    <div>
        @Html.LabelFor(x => x.MinValue)
        @Html.EditorFor(x => x.MinValue)
    </div>
    <div>
        @Html.LabelFor(x => x.MaxValue)
        @Html.EditorFor(x => x.MaxValue)
    </div>
    <button type="submit">OK</button>
}

显然,自定义适配器注册应该在外部 JavaScript 文件中执行,以避免污染 View ,但为了本文的目的和简洁性,我将其放在 View 中。

关于asp.net-mvc - MVC 动态值的不引人注目的范围验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7851035/

相关文章:

c# - ASP.net core viewmodel 字段是必需的,尽管它可以为 null

c# - DropDownListFor 如何从数据库设置默认值

jquery - MVC 3,重用部分 View 和jquery,不与DOM冲突

asp.net-mvc - MVC3 的奇怪名称远程验证

javascript - 如何设置 Kendo 窗口的内容?

javascript - 基于MVC Area的Concat文件

c# - 使用 Unity 2 和 MVC 3 将类注入(inject)身份验证属性

asp.net - 在 ASP.NET MVC 3 中更改 Membership.ValidateUser() 和其他内容

asp.net-mvc - 没有和 100 之间的范围注释?

Silverlight DataGrid - 在未编辑单个单元格时验证新行