【问题标题】:IValidatableObject Validate() for different scenariosIValidatableObject Validate() 用于不同的场景
【发布时间】:2012-06-23 14:42:47
【问题描述】:

我一直在模型实体上实现 IValidatableObject,并使用 Validate(ValidationContext) 执行验证,通常很复杂。

我可以使用 ValidationContext 来区分不同的验证场景吗?

例如以我有 3 个验证场景的用户模型为例:

  • 注册 - 我想测试一封电子邮件是否唯一,并且已输入一小部分必填字段
  • 更改详细信息 - 不同的电子邮件唯一性检查,注册后需要更多详细信息,此处不更改密码,因此不需要检查
  • 更改密码 - 仅验证密码字段

这是一个适当的用途吗?如果是这样,如何确保在帖子之后和调用 Validate() 之前设置正确的 ValidationContext 属性?还是我应该采取完全不同的方法?

【问题讨论】:

  • 我不明白你为什么要使用ValidationContext?
  • 我真的在问它是否可以用于此目的。但是,如果这是不好的做法或者真的不打算用于识别“验证组”,那么请告诉我。

标签: asp.net-mvc-3 validation


【解决方案1】:

IValidatableObject 用于针对单个模型执行多个验证。在您的情况下,您有一个 User 模型,并且您想要进行三个验证,您可以通过在 User 模型中实现 IValidatableObject 来完美地做到这一点。

ValidationContext 并没有带来太多好处(除了提供对上下文的访问),因为我们可以直接在 Validate 方法中访问所有属性。

IValidatableObject 执行与单个模型相关的多个验证的示例。 (那么这里ValidationContext有什么用?)

public class Party : IValidatableObject
{
    [Required(ErrorMessage = "Start date is required")]
    [FutureDateValidator(ErrorMessage = "Start date should be a future date")]
    public DateTime StartDate { get; set; }

    [Required(ErrorMessage = "Duration is required")]    
    public int DurationInHours { get; set; }

    [Required(ErrorMessage = "No. of joinees is required")]
    [Range(2, 10, ErrorMessage = "No. of joinees should be minimum 2 and not more than 10")]
    public int NoOfJoinees { get; set; }    

    public bool Drinks { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (StartDate.TimeOfDay > new TimeSpan(22 - DurationInHours, 0, 0))
        {
            yield return new ValidationResult("The party should not exceed after 10.00 PM");
        }

        if (NoOfJoinees < 5 && Drinks)
        {
            yield return new ValidationResult("Drinks are only allowed if no. of joinees is 5 or more.");
        }
    }
}

【讨论】:

  • 好的,我可能正在尝试将 ValidationContext 用于它不适合做的事情,但是您将如何处理 Validate() 中的 3 个场景?您会添加一个属性/隐藏字段来维护可以在 Validate() 中引用的当前“操作”(Signup、Edit、ChangePass),还是之后在页面控制器中执行?我想在 Validate() 中进行所有验证。
【解决方案2】:

对于我的两分钱,我想说您的模型要么处于有效状态(应用所有验证标准),要么不是。如果在某些情况下,您不想应用验证,那么我认为您真的应该使用单独的模型(实际上是 ViewModel)。

在您的示例中,我将创建一个 RegisterViewModel 用于注册,并创建一个单独的 EditUserViewModel 用于更改详细信息。然后,它们中的每一个都将拥有自己的验证,并且它们将拥有single responsibility

创建一个可以在许多不同视图中重复使用的胖模型,恕我直言,有点代码味道。我有很多理由这么想。首先,假设您有一个模型用于与用户数据的所有交互。它看起来像这样:

public class UserModel
{
    public int UserId { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
    public bool IsAdministrator { get; set; }
}

稍后您决定跟踪在网站注册期间使用的浏览器。你在哪里添加?它真的与用户无关,所以它不应该出现在UserModel 模型上。如果您有单独的RegisterViewModel,您可以在注册过程发生变化时根据需要对其进行修改,而不必担心它会如何影响使用它的其他地方。

例如,如果您将上述模型与 MVC 的 DefaultModelBinder 一起使用,则会出现更严重的问题。如here 所述,即使您在表单上没有IsAdministrator 字段(通过利用批量分配漏洞),用户也可以创建自己的请求并授予自己管理员权限。同样,如果在没有 IsAdministrator 属性的情况下使用单独的 ViewModel,它将减少安全漏洞的表面积。

以上只是一个例子,但我相信你明白了。

【讨论】:

  • 我理解你的观点,但我并不热衷于为不同的验证场景复制具有相同或相似属性的模型,然后将每个模型重新协调回单个数据库对象。它引入了更多的工作和分散的代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-28
  • 2013-04-22
  • 1970-01-01
  • 1970-01-01
  • 2015-01-08
  • 1970-01-01
相关资源
最近更新 更多