Die*_*ego 8 asp.net asp.net-mvc data-annotations asp.net-mvc-3 ivalidatableobject
我有一个ViewModel,它有一些DataAnnotations验证,然后对于更复杂的验证实现IValidatableObject并使用Validate方法.
我期待的行为是这样的:首先是所有DataAnnotations,然后,只有在没有错误的情况下,验证方法.我怎么发现这并非总是如此.我的ViewModel(演示版)有一个string,一个decimal和一个文件decimal?.所有这三个属性都只有Required属性.对于string和,decimal?行为是预期的行为,但对于decimal,当为空时,必需的验证失败(到目前为止很好),然后执行Validate方法.如果我检查属性,它的值为零.
这里发生了什么?我错过了什么?
注意:我知道必须使用Required属性来检查值是否为null.所以我希望被告知不要在不可空类型中使用Required属性(因为它不会触发),或者,某种程度上该属性理解POST值并注意该字段未被填充.在第一种情况下,属性不应该触发,并且应该触发Validate方法.在第二种情况下,属性应该触发,并且不应触发Validate方法.但我的结果是:属性触发器和Validate方法触发.
这是代码(没什么特别的):
控制器:
public ActionResult Index()
{
return View(HomeModel.LoadHome());
}
[HttpPost]
public ActionResult Index(HomeViewModel viewModel)
{
try
{
if (ModelState.IsValid)
{
HomeModel.ProcessHome(viewModel);
return RedirectToAction("Index", "Result");
}
}
catch (ApplicationException ex)
{
ModelState.AddModelError(string.Empty, ex.Message);
}
catch (Exception ex)
{
ModelState.AddModelError(string.Empty, "Internal error.");
}
return View(viewModel);
}
Run Code Online (Sandbox Code Playgroud)
模型:
public static HomeViewModel LoadHome()
{
HomeViewModel viewModel = new HomeViewModel();
viewModel.String = string.Empty;
return viewModel;
}
public static void ProcessHome(HomeViewModel viewModel)
{
// Not relevant code
}
Run Code Online (Sandbox Code Playgroud)
视图模型:
public class HomeViewModel : IValidatableObject
{
[Required(ErrorMessage = "Required {0}")]
[Display(Name = "string")]
public string String { get; set; }
[Required(ErrorMessage = "Required {0}")]
[Display(Name = "decimal")]
public decimal Decimal { get; set; }
[Required(ErrorMessage = "Required {0}")]
[Display(Name = "decimal?")]
public decimal? DecimalNullable { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
yield return new ValidationResult("Error from Validate method");
}
}
Run Code Online (Sandbox Code Playgroud)
视图:
@model MVCTest1.ViewModels.HomeViewModel
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
@using (Html.BeginForm(null, null, FormMethod.Post))
{
<div>
@Html.ValidationSummary()
</div>
<label id="lblNombre" for="Nombre">Nombre:</label>
@Html.TextBoxFor(m => m.Nombre)
<label id="lblDecimal" for="Decimal">Decimal:</label>
@Html.TextBoxFor(m => m.Decimal)
<label id="lblDecimalNullable" for="DecimalNullable">Decimal?:</label>
@Html.TextBoxFor(m => m.DecimalNullable)
<button type="submit" id="aceptar">Aceptar</button>
<button type="submit" id="superAceptar">SuperAceptar</button>
@Html.HiddenFor(m => m.Accion)
}
Run Code Online (Sandbox Code Playgroud)
Joa*_*oao 14
评论交换后的注意事项:
开发人员之间的共识和预期行为是,只有在没有触发验证属性的情况下才会调用IValidatableObject方法Validate().简而言之,预期的算法就是这个(取自上一个链接):
但是,使用问题代码,Validate甚至在[Required]触发后调用.这似乎是一个明显的MVC错误.这是报道在这里.
三种可能的解决方法:
除了打破MVC预期的行为之外,这里有一个解决方法,虽然它的使用存在一些陈述的问题.通过一些更改以避免在同一字段中显示多个错误,代码如下:
viewModel
.Validate(new ValidationContext(viewModel, null, null))
.ToList()
.ForEach(e => e.MemberNames.ToList().ForEach(m =>
{
if (ModelState[m].Errors.Count == 0)
ModelState.AddModelError(m, e.ErrorMessage);
}));
Run Code Online (Sandbox Code Playgroud)忘记IValidatableObject并仅使用属性.它干净,直接,更好地处理本地化,并且最重要的是它可以在所有型号中重复使用.只需为要执行的每个验证实现ValidationAttribute.您可以验证所有模型或特定属性,这取决于您.除了默认可用的属性(DataType,Regex,Required和所有这些东西)之外,还有几个具有最常用验证的库.实现"缺失的"的是FluentValidation.
IValidatableObject实现丢弃数据注释的接口.如果它是一个非常特殊的模型并且不需要太多验证,这似乎是一个合理的选择.在大多数情况下,开发人员将执行所有常规和通用验证(即必需等),如果使用了属性,则会导致默认情况下已经实现的验证上的代码重复.也没有可重用性.评论前回答:
首先,我从头开始创建了一个新项目,只提供了您提供的代码.它永远不会同时触发数据注释和Validate方法.
无论如何,知道这一点,
根据设计,MVC3将[Required]属性添加到非可空值类型中,例如int,DateTime或者是,decimal.所以,即使你从中删除了必需的属性,decimal它也就像那里一样.
这是因为它的错误(或不是),但它的设计方式存在争议.
在你的例子中:
decimal.在您的Application_Start方法中,可能会关闭此行为:
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
Run Code Online (Sandbox Code Playgroud)
我猜这个属性的名字是不言自明的.
无论如何,我不明白为什么你想让用户输入不需要的东西,不要让那个属性可以为空.如果它是null,那么检查它是你的工作,如果在验证之前你不想在控制器内做空.
public ActionResult Index(HomeViewModel viewModel)
{
// Complete values that the user may have
// not filled (all not-required / nullables)
if (viewModel.Decimal == null)
{
viewModel.Decimal = 0m;
}
// Now I can validate the model
if (ModelState.IsValid)
{
HomeModel.ProcessHome(viewModel);
return RedirectToAction("Ok");
}
}
Run Code Online (Sandbox Code Playgroud)
您认为这种方法有什么不对,或者不应该这样?
| 归档时间: |
|
| 查看次数: |
10094 次 |
| 最近记录: |