gpt4 book ai didi

asp.net - IValidatableObject 验证方法在 DataAnnotations 失败时触发

转载 作者:行者123 更新时间:2023-12-04 05:56:38 29 4
gpt4 key购买 nike

我有一个 ViewModel,它有一些 DataAnnotations 验证,然后为了更复杂的验证实现 IValidatableObject 并使用 Validate 方法。

我期待的行为是 this one :首先是所有的 DataAnnotations,然后,只有在没有错误的情况下,才使用 Validate 方法。我怎么发现这并不总是正确的。我的 ViewModel(一个演示版)有三个文件一个 string , 一个 decimal和一个 decimal? .所有三个属性都只有Required 属性。对于stringdecimal?该行为是预期的,但对于 decimal ,当为空时,Required 验证失败(到目前为止还不错),然后执行 Validate 方法。如果我检查该属性,它的值为零。

这里发生了什么?我错过了什么?

注意:我知道Required 属性是假设检查值是否为空。所以我希望被告知不要在不可为空的类型中使用 Required 属性(因为它永远不会触发),或者,该属性以某种方式理解 POST 值并注意该字段未填充。在第一种情况下,该属性不应触发,而应触发 Validate 方法。在第二种情况下,属性应该触发并且 Validate 方法不应该触发。但我的结果是:属性触发和 Validate 方法触发。

这是代码(没什么特别的):

Controller :

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);
}

模型:
public static HomeViewModel LoadHome()
{
HomeViewModel viewModel = new HomeViewModel();
viewModel.String = string.Empty;
return viewModel;
}

public static void ProcessHome(HomeViewModel viewModel)
{
// Not relevant code
}

View 模型:
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");
}
}

看法:
@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)
}

最佳答案

意见交流后的注意事项:

双方同意和expected behavior among developersIValidatableObject的方法Validate()仅在未触发验证属性时才调用。简而言之,预期的算法是这样的(取自上一个链接):

  • 验证属性级属性
  • 如果任何验证器无效,则中止验证并返回失败
  • 验证对象级属性
  • 如果任何验证器无效,则中止验证并返回失败
  • 如果在桌面框架上并且对象实现了 IValidatableObject,则调用其 Validate 方法并返回任何失败

  • 但是, 使用问题的代码,Validate甚至在 [Required] 之后调用触发器 .这似乎是一个明显的 MVC 错误 .报道 here .

    三种可能的解决方法:
  • 有一个解决方法 here尽管除了破坏 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);
    }));
  • 忘记IValidatableObject并且只使用属性。它干净、直接、更好地处理本地化,最重要的是它可以在所有模型中重复使用。只需执行ValidationAttribute对于您想要执行的每个验证。您可以验证所有模型或特定属性,这取决于您。除了默认可用的属性(DataType、Regex、Required 和所有这些东西)之外,还有几个库具有最常用的验证。实现“缺失”的一种是FluentValidation .
  • 仅实现 IValidatableObject界面扔掉data annotations .如果它是一个非常特殊的模型并且不需要太多验证,这似乎是一个合理的选择。在大多数情况下,开发人员将执行所有常规和常见验证(即必需等),如果使用属性,则会导致默认情况下已经实现的验证代码重复。也没有可重用性。


  • 评论前回答:

    首先,我只使用您提供的代码从头开始创建了一个新项目。它从不同时触发数据注释和验证方法。

    无论如何,知道这一点,

    根据设计,MVC3 添加了 [Required]不可空值类型的属性,例如 int , DateTime或者,是的, decimal .因此,即使您从该 decimal 中删除所需的属性它就像它在那里一样工作。

    这对于它的错误(或不是)是有争议的,但它的设计方式。

    在你的例子中:
  • 如果存在 [Required] 并且没有给出值,则触发“DataAnnotation”。从我的角度来看完全可以理解
  • 如果不存在 [Required] 但值不可为空,则“DataAnnotation”触发。值得商榷,但我倾向于同意它,因为如果属性不可为空,则必须输入一个值,否则不要将其显示给用户或只使用可空的 decimal .

  • 看起来,可以在您的 Application_Start 方法中关闭此行为:
    DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;

    我想该属性(property)的名称是不言自明的。

    无论如何,我不明白您为什么要让用户输入不需要的内容并且不要使该属性为空。如果它为空,那么您的工作就是检查它,如果您不希望它为空,则在验证之前,在 Controller 中。
    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");
    }
    }

    您认为这种方法有什么问题或不应该这样?

    关于asp.net - IValidatableObject 验证方法在 DataAnnotations 失败时触发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8153602/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com