MVC3输入验证依赖 [英] MVC3 Input Dependent Validation

查看:112
本文介绍了MVC3输入验证依赖的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请注意:我是比较新的MVC3结果。
输入验证似乎是pretty此框架下,在那里你可以说[必填]和客户端和服务器端验证从那里只是工作不错。但是,如果我想什么来实现有条件的验证?

场景:我会要求您选择2选项之一投寄箱箱。如果选择选项1,2文本输入字段将出现,并且两者都需要。如果选项2被选择,2个单选按钮就会出现,你需要选择其中之一。如何才能验证MVC3实现这一目标?

显然,在模型中,我们不能仅仅有标准所要求的验证,某些领域不会,这取决于下拉我们选择的选项提交。


解决方案

  

输入验证似乎是pretty不错这一框架


真的吗?您所描述的情况是使用数据注释进行验证的局限性,一个完美的例子。

我会尝试探索3可能的技术。转到这个答案的结束和一个我用的是第三个技巧和建议。

让我开始探索它们显示控制器将用于3的方案,因为他们将是相同的看法了。

控制器:

 公共类HomeController的:控制器
{
    公众的ActionResult指数()
    {
        返回查看(新MyViewModel());
    }    [HttpPost]
    公众的ActionResult指数(MyViewModel模型)
    {
        返回查看(模型);
    }
}

查看:

  @model MyViewModel@using(Html.BeginForm())
{
    < D​​IV>
        @ Html.LabelFor(X => x.SelectedOption)
        @ Html.DropDownListFor(
            X => x.SelectedOption,
            Model.Options,
             - 选择一个选项 -
            新{ID =optionSelector}
        )
        @ Html.ValidationMessageFor(X => x.SelectedOption)
    < / DIV>
    < D​​IV ID =输入@ Html.Raw(!?Model.SelectedOption =1的风格= \\显示:无; \\:)>
        @ Html.LabelFor(X => x.Input1)
        @ Html.EditorFor(X => x.Input1)
        @ Html.ValidationMessageFor(X => x.Input1)        @ Html.LabelFor(X => x.Input2)
        @ Html.EditorFor(X => x.Input2)
        @ Html.ValidationMessageFor(X => x.Input2)
    < / DIV>
    < D​​IV ID =收音机@ Html.Raw(!?Model.SelectedOption =2的风格= \\显示:无; \\:)>
        @ Html.Label(RAD1,值1)
        @ Html.RadioButtonFor(X => x.RadioButtonValue,值1,新{ID =RAD1})        @ Html.Label(RAD2,值2)
        @ Html.RadioButtonFor(X => x.RadioButtonValue,值2,新{ID =RAD2})        @ Html.ValidationMessageFor(X => x.RadioButtonValue)
    < / DIV>
    <按钮式=提交>确定< /按钮>
}

脚本:

  $(函数(){
    $('#optionSelector')。改变(函数(){
        VAR值= $(本).VAL();
        $('#输入')切换(价值==='1');
        $('#电台)切换(价值==='2');
    });
});

没什么特别的在这里。控制器实例化传递到视图的视图模型。在视图,我们有一个表单和一个DropDownList。使用JavaScript,我们同意这个dropdownlisty的变化事件,触发这种形式的基础上,选定值的不同区域。


可能性1

第一种可能是让你的视图模型实施<一个href=\"http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.ivalidatableobject.aspx\">IValidatableObject.记住,如果你决定要实施您的视图模型这个界面,你不应该使用您的视图模型属性或验证方法的任何属性验证,将永远不会被调用:

 公共类MyViewModel:IValidatableObject
{
    公共字符串SelectedOption {搞定;组; }
    公共IEnumerable的&LT; SelectListItem&GT;选项
    {
        得到
        {
            返回新的[]
            {
                新SelectListItem {值=1,文本=项目1},
                新SelectListItem {值=2,文本=项目2},
            };
        }
    }    公共字符串RadioButtonValue {搞定;组; }    公共字符串输入1 {搞定;组; }
    公共字符串输入2 {搞定;组; }    公共IEnumerable的&LT;&为ValidationResult GT;验证(ValidationContext validationContext)
    {
        如果(SelectedOption ==1)
        {
            如果(string.IsNullOrEmpty(输入1))
            {
                产量返回新的ValidationResult(
                    输入1是必需的,
                    新的[] {输入1}
                );
            }
            如果(string.IsNullOrEmpty(输入2))
            {
                产量返回新的ValidationResult(
                    输入2是必需的,
                    新的[] {输入2}
                );
            }
        }
        否则如果(SelectedOption ==2)
        {
            如果(string.IsNullOrEmpty(RadioButtonValue))
            {
                产量返回新的ValidationResult(
                    RadioButtonValue是必需的,
                    新的[] {RadioButtonValue}
                );
            }
        }
        其他
        {
            产量返回新的ValidationResult(
                你必须至少选择一个选项
                新的[] {SelectedOption}
            );
        }
    }
}

什么是对这种做法是很好,你可以处理任何复杂的验证场景。什么是坏对这种做法的是,它为我们与消息和错误输入字段名称选择混合验证不是很可读。


2的可能性

另一种可能性是编写自定义验证属​​性,比如 [RequiredIf]

  [AttributeUsageAttribute(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,的AllowMultiple = TRUE)]
公共类RequiredIfAttribute:RequiredAttribute标签
{
    私人字符串OtherProperty {搞定;组; }
    私有对象条件{搞定;组; }    公共RequiredIfAttribute(字符串otherProperty,对象条件)
    {
        OtherProperty = otherProperty;
        条件=状态;
    }    保护覆盖的ValidationResult的IsValid(对象的值,ValidationContext validationContext)
    {
        VAR财产= validationContext.ObjectType.GetProperty(OtherProperty);
        如果(属性== NULL)
            返回新的ValidationResult(的String.Format(物权{0}没有找到。OtherProperty));        VAR为PropertyValue = property.GetValue(validationContext.ObjectInstance,NULL);
        VAR conditionIsMet =等于(为PropertyValue,条件);
        返回conditionIsMet? base.IsValid(值,validationContext):空;
    }
}

和则:

 公共类MyViewModel
{
    [需要]
    公共字符串SelectedOption {搞定;组; }
    公共IEnumerable的&LT; SelectListItem&GT;选项
    {
        得到
        {
            返回新的[]
            {
                新SelectListItem {值=1,文本=项目1},
                新SelectListItem {值=2,文本=项目2},
            };
        }
    }    [RequiredIf(SelectedOption,2)]
    公共字符串RadioButtonValue {搞定;组; }    [RequiredIf(SelectedOption,1)]
    公共字符串输入1 {搞定;组; }
    [RequiredIf(SelectedOption,1)]
    公共字符串输入2 {搞定;组; }
}

什么是对这种做法是好的,我们的视图模型是干净的。什么是不好这是使用自定义属性验证,你可能会迅速打的限制。想想例子更加复杂的情况下,您将需要向下递归到子模型和集合之类的东西。这将很快成为一个烂摊子。


可能性3

第三种可能性是使用 FluentValidation.NET 。这是我个人使用和推荐。

所以:


  1. 安装封装FluentValidation.MVC3 在你的NuGet控制台

  2. 的Application_Start 的Global.asax 添加以下行:

      FluentValidationModelValidatorProvider.Configure();


  3. 写出视图模型的验证:

     公共类MyViewModelValidator:AbstractValidator&LT; MyViewModel&GT;
    {
        公共MyViewModelValidator()
        {
            RuleFor(X =&GT; x.SelectedOption).NotEmpty();
            RuleFor(X =&GT; x.Input1).NotEmpty()等。(X =&GT; x.SelectedOption ==1);
            RuleFor(X =&GT; x.Input2).NotEmpty()等。(X =&GT; x.SelectedOption ==1);
            RuleFor(X =&GT; x.RadioButtonValue).NotEmpty()等。(X =&GT; x.SelectedOption ==2);
        }
    }


  4. 和视图模型本身是一个POCO:

      [验证(typeof运算(MyViewModelValidator))]
    公共类MyViewModel
    {
        公共字符串SelectedOption {搞定;组; }
        公共IEnumerable的&LT; SelectListItem&GT;选项
        {
            得到
            {
                返回新的[]
                {
                    新SelectListItem {值=1,文本=项目1},
                    新SelectListItem {值=2,文本=项目2},
                };
            }
        }    公共字符串RadioButtonValue {搞定;组; }
        公共字符串输入1 {搞定;组; }
        公共字符串输入2 {搞定;组; }
    }


有什么好的,这是我们有验证和视图模型之间的完美分离。它<一个href=\"http://fluentvalidation.$c$cplex.com/wikipage?title=mvc&referringTitle=Documentation\">integrates很好地与ASP.NET MVC 。我们可以unit测试的我们孤立验证在一个非常容易和流利的方式。

有什么坏处是,当微软在设计ASP.NET MVC他们选择了,而不是命令式声明的验证逻辑(使用数据注释),这是更适合来验证场景,并能处理一切事情。这是糟糕,FluentValidation.NET实际上不是的的标准方式以在ASP.NET MVC中执行验证。

Note: I'm relatively new to MVC3.
Input validation seems to be pretty nice with this framework, where you can just say [Required] and both client and server side validation just works from there. But what if I would like to implement conditional validation?

Scenario: I will have a dropbox box that requires you to choose one of 2 options. If option 1 is selected, 2 text input fields will appear and both are required. If option 2 is selected, 2 radio buttons will appear and you are required to select one of them. How can MVC3 validation achieve this?

Obviously, in the Model we can't have just standard required validation, as some fields won't be submitted depending on which drop-down option we choose.

解决方案

Input validation seems to be pretty nice with this framework

Really? The scenario you describe is a perfect example of the limitations of using data annotations for validation.

I will try to explore 3 possible techniques. Go to the end of this answer and the third technique for the one I use and recommend.

Let me just before start exploring them show the controller and the view that will be used for the 3 scenarios as they will be the same.

Controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View(new MyViewModel());
    }

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

View:

@model MyViewModel

@using (Html.BeginForm())
{
    <div>
        @Html.LabelFor(x => x.SelectedOption)
        @Html.DropDownListFor(
            x => x.SelectedOption, 
            Model.Options, 
            "-- select an option --", 
            new { id = "optionSelector" }
        )
        @Html.ValidationMessageFor(x => x.SelectedOption)
    </div>
    <div id="inputs"@Html.Raw(Model.SelectedOption != "1" ? " style=\"display:none;\"" : "")>
        @Html.LabelFor(x => x.Input1)   
        @Html.EditorFor(x => x.Input1)
        @Html.ValidationMessageFor(x => x.Input1)

        @Html.LabelFor(x => x.Input2)
        @Html.EditorFor(x => x.Input2)
        @Html.ValidationMessageFor(x => x.Input2)
    </div>
    <div id="radios"@Html.Raw(Model.SelectedOption != "2" ? " style=\"display:none;\"" : "")>
        @Html.Label("rad1", "Value 1")
        @Html.RadioButtonFor(x => x.RadioButtonValue, "value1", new { id = "rad1" })

        @Html.Label("rad2", "Value 2")
        @Html.RadioButtonFor(x => x.RadioButtonValue, "value2", new { id = "rad2" })

        @Html.ValidationMessageFor(x => x.RadioButtonValue)
    </div>
    <button type="submit">OK</button>
}

script:

$(function () {
    $('#optionSelector').change(function () {
        var value = $(this).val();
        $('#inputs').toggle(value === '1');
        $('#radios').toggle(value === '2');
    });
});

Nothing fancy here. A controller that instantiates a view model that is passed to the view. In the view we have a form and a dropdownlist. Using javascript we subscribe to the change event of this dropdownlisty and toggle different regions of this form based on the selected value.


Possibility 1

The first possibility is to have your view model implement the IValidatableObject. Bear in mind that if you decide to implement this interface on your view model you shouldn't use any validation attributes on your view model properties or the Validate method will never be invoked:

public class MyViewModel: IValidatableObject
{
    public string SelectedOption { get; set; }
    public IEnumerable<SelectListItem> Options
    {
        get
        {
            return new[]
            {
                new SelectListItem { Value = "1", Text = "item 1" },
                new SelectListItem { Value = "2", Text = "item 2" },
            };
        }
    }

    public string RadioButtonValue { get; set; }

    public string Input1 { get; set; }
    public string Input2 { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (SelectedOption == "1")
        {
            if (string.IsNullOrEmpty(Input1))
            {
                yield return new ValidationResult(
                    "Input1 is required", 
                    new[] { "Input1" }
                );
            }
            if (string.IsNullOrEmpty(Input2))
            {
                yield return new ValidationResult(
                    "Input2 is required",
                    new[] { "Input2" }
                );
            }
        }
        else if (SelectedOption == "2")
        {
            if (string.IsNullOrEmpty(RadioButtonValue))
            {
                yield return new ValidationResult(
                    "RadioButtonValue is required",
                    new[] { "RadioButtonValue" }
                );
            }
        }
        else
        {
            yield return new ValidationResult(
                "You must select at least one option", 
                new[] { "SelectedOption" }
            );
        }
    }
}

What's nice about this approach is that you could handle any complex validation scenario. What's bad about this approach is that it's not quite readable as we are mixing validation with messages and error input field name selection.


Possibility 2

Another possibility is to write a custom validation attribute like [RequiredIf]:

[AttributeUsageAttribute(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = true)]
public class RequiredIfAttribute : RequiredAttribute
{
    private string OtherProperty { get; set; }
    private object Condition { get; set; }

    public RequiredIfAttribute(string otherProperty, object condition)
    {
        OtherProperty = otherProperty;
        Condition = condition;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var property = validationContext.ObjectType.GetProperty(OtherProperty);
        if (property == null)
            return new ValidationResult(String.Format("Property {0} not found.", OtherProperty));

        var propertyValue = property.GetValue(validationContext.ObjectInstance, null);
        var conditionIsMet = Equals(propertyValue, Condition);
        return conditionIsMet ? base.IsValid(value, validationContext) : null;
    }
}

and then:

public class MyViewModel
{
    [Required]
    public string SelectedOption { get; set; }
    public IEnumerable<SelectListItem> Options
    {
        get
        {
            return new[]
            {
                new SelectListItem { Value = "1", Text = "item 1" },
                new SelectListItem { Value = "2", Text = "item 2" },
            };
        }
    }

    [RequiredIf("SelectedOption", "2")]
    public string RadioButtonValue { get; set; }

    [RequiredIf("SelectedOption", "1")]
    public string Input1 { get; set; }
    [RequiredIf("SelectedOption", "1")]
    public string Input2 { get; set; }
}

What's nice about this approach is that our view model is clean. What's bad about this is that using custom validation attributes you might quickly hit the limits. Think for example more complex scenarios where you would need to recurse down to sub-models and collections and stuff. This will quickly become a mess.


Possibility 3

A third possibility is to use FluentValidation.NET. It's what I personally use and recommend.

So:

  1. Install-Package FluentValidation.MVC3 in your NuGet console
  2. In Application_Start in your Global.asax add the following line:

    FluentValidationModelValidatorProvider.Configure();
    

  3. Write a validator for the view model:

    public class MyViewModelValidator : AbstractValidator<MyViewModel>
    {
        public MyViewModelValidator()
        {
            RuleFor(x => x.SelectedOption).NotEmpty();
            RuleFor(x => x.Input1).NotEmpty().When(x => x.SelectedOption == "1");
            RuleFor(x => x.Input2).NotEmpty().When(x => x.SelectedOption == "1");
            RuleFor(x => x.RadioButtonValue).NotEmpty().When(x => x.SelectedOption == "2");
        }
    }
    

  4. And the view model itself is a POCO:

    [Validator(typeof(MyViewModelValidator))]
    public class MyViewModel
    {
        public string SelectedOption { get; set; }
        public IEnumerable<SelectListItem> Options
        {
            get
            {
                return new[]
                {
                    new SelectListItem { Value = "1", Text = "item 1" },
                    new SelectListItem { Value = "2", Text = "item 2" },
                };
            }
        }
    
        public string RadioButtonValue { get; set; }
        public string Input1 { get; set; }
        public string Input2 { get; set; }
    }
    

What's good about this is that we have a perfect separation between the validation and the view model. It integrates nicely with ASP.NET MVC. We can unit test our validator in isolation in a very easy and fluent way.

What's bad about this is that when Microsoft were designing ASP.NET MVC they opted for declarative validation logic (using data annotations) instead of imperative which is much better suited to validation scenarios and can handle just anything. It's bad that FluentValidation.NET is not actually the standard way to perform validation in ASP.NET MVC.

这篇关于MVC3输入验证依赖的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆