ASP.NET MVC:数据注释验证是不是足够?

Posted

技术标签:

【中文标题】ASP.NET MVC:数据注释验证是不是足够?【英文标题】:ASP.NET MVC: Is Data Annotation Validation Enough?ASP.NET MVC:数据注释验证是否足够? 【发布时间】:2010-12-06 05:15:39 【问题描述】:

我在 ASP.NET MVC 2 中广泛使用 Data Annotation 验证。这个新功能节省了大量时间,因为我现在能够在一个地方定义客户端验证和服务器端验证.然而,当我做一些详细的测试时,我意识到如果我单独依赖数据注释验证,那么有人很容易绕过服务器端验证。例如,如果我通过使用 [Required] 属性注释属性来定义一个必填字段,并在表单中为该必填字段放置一个文本框,用户可以简单地从 DOM 中删除该文本框(这可以通过 Firebug 轻松完成)现在,在 Controller 内部的 ModelBinding 期间,不会在该属性上触发 Data Annotation 验证。为确保触发“必需”验证,我可以在 ModelBinding 发生后重复验证,但随后我会重复验证逻辑。

大家对验证有什么建议?数据注释验证是否足够?还是需要重复验证以确保在所有情况下都能触发验证?

后续评论: 根据下面的答案,我似乎不能单独依赖模型绑定器和数据注释验证。由于我们得出的结论是需要额外的服务器端验证,我的服务层是否有一种简单的方法可以根据数据注释中定义的内容触发验证?看来这会让我们两全其美……我们不需要重复验证代码,但即使 Model Binder 没有触发验证,我们仍然会确保执行验证。

我将把这个后续评论作为一个单独的问题发布,因为它提出的问题与原来的问题不同。

【问题讨论】:

Koritnik 的回复回答了您的后续查询。我的验证类似于他发布的回复。相同的 DataAnnotation 定义可用于服务器和客户端验证。 如果提供的验证属性和框架本身适合您,则数据注释验证很好。由于社区反馈,ASP.NET MVC 2 RTM 的Required 行为已更改,因此 [Required] 现在可以按预期工作。可选地,查看:Validation Block (Enterprise Library)、xVal、NHibernate Validators(据说不依赖于 NHibernate ORM)。 "I'm going to post this follow-up comment as a separate question, as it poses a different question than the original one." 链接到那不是一个坏主意,嗯? 【参考方案1】:

我认为要对安全性保持警惕,您应该选择将服务器验证作为优先事项,并确保这始终是您的后备方案。您的服务器验证应该在没有客户端验证的情况下工作。客户端验证更多地用于 UX,尽管这对您的设计至关重要,但它仅次于安全性。考虑到这一点,您会发现自己在重复您的验证。一个目标通常是尝试设计您的应用程序,以便尽可能集成服务器和客户端验证,以减少在服务器和客户端上进行验证所需的工作。但请放心,您必须两者都做。

如果绕过客户端验证(通过 DOM 操作)正在避免服务器验证(您似乎在指出),那么您的服务器验证可能无法正确使用。您应该在控制器操作或服务层中再次调用服务器验证。您描述的场景不应该破坏您的服务器验证。

对于您描述的场景,DataAnnotation 属性方法应该足够了。您似乎只需要进行一些代码更改,以确保在提交表单时也调用您的服务器验证。

【讨论】:

【参考方案2】:

我将 xVal 与 DataAnnotations 配对,并编写了自己的操作过滤器来检查任何实体类型参数以进行验证。因此,如果回发中缺少某些字段,则此验证器将填充 ModelState 字典,从而使模型无效。

先决条件:

我的实体/模型对象都实现了IObjectValidator 接口,该接口声明了Validate() 方法。 我的属性类叫做ValidateBusinessObjectAttribute xVal 验证库

动作过滤代码:

public void OnActionExecuting(ActionExecutingContext filterContext)

    IEnumerable<KeyValuePair<string, object>> parameters = filterContext.ActionParameters.Where<KeyValuePair<string, object>>(p => p.Value.GetType().Equals(this.ObjectType ?? p.Value.GetType()) && p.Value is IObjectValidator);
    foreach (KeyValuePair<string, object> param in parameters)
    
        object value;
        if ((value = param.Value) != null)
        
            IEnumerable<ErrorInfo> errors = ((IObjectValidator)value).Validate();
            if (errors.Any())
            
                new RulesException(errors).AddModelStateErrors(filterContext.Controller.ViewData.ModelState, param.Key);
            
        
    

我的控制器动作是这样定义的:

[ValidateBusinessObject]
public ActionResult Register(User user, Company company, RegistrationData registrationData)

    if (!this.ModelState.IsValid)
    
        return View();
    
    ...

【讨论】:

你有更详细的例子来说明如何使用这个或一个可下载的项目 @geocine:问题出在哪里?你在使用 MVC1 吗?较新的版本不需要这样做,因为它们会自动验证强类型参数......但是这里的这个例子和它应该在现实中一样详细。那么问题似乎出在哪里? 我刚刚路过,我是 aspmvc 的新手,正在阅读有关验证问题的信息。我忘了我使用的是 MVC 2。我的错。【参考方案3】:

DataAnnotation 肯定是不够的。我还广泛使用它来预先验证我对域模型的调用,以获得更好的错误报告并尽早失败。

但是,您可以自己调整 DataAnnotation 模型,以确保必须发布带有 [Required] 的属性。 (将在今天晚些时候跟进代码)。

更新 获取DataAnnotations Model Binder的源码,在DataAnnotationsModelBinder.cs中找到这一行

// Only bind properties that are part of the request
if (bindingContext.ValueProvider.DoesAnyKeyHavePrefix(fullPropertyKey)) 

改成

// Only bind properties that are part of the request
bool contextHasKey = bindingContext.ValueProvider.DoesAnyKeyHavePrefix(fullPropertyKey);
bool isRequired = GetValidationAttributes(propertyDescriptor).OfType<RequiredAttribute>().Count() > 0;
if (contextHasKey || (!contextHasKey && isRequired)) 

【讨论】:

当然,我在上班前发布了这个(现在还在工作)所以没有时间编码:(。我之前稍微修改了活页夹,因为它没有检查嵌套对象并重置无效属性将我不同意的 null 参见 ***.com/questions/820468/… 从那时起我也添加了必需的检查,但我想在我回家后再发布它们之前对其进行测试。 已用代码更新,但我无法正确测试它,明天再做一次,但把它贴出来,也许你可以更快地评估它。它通过了项目的单元测试,但公平地说,这个案例没有测试测试:D。 谢谢 Martijn。我将不得不将其留到另一天,因为这将需要对源进行更改。现在,我将采纳您(和其他人)的建议,并确保在域模型中也重复验证。 当然没问题。如果您可以将数据完整性检查卸载到 DataAnnotations,那就太好了。这样,您的域模型只需验证业务规则。 域模型仍然需要调用 DataAnnotations,但你永远不知道何时要将域导出为单独的 DLL :)。【参考方案4】:

我通过从 xVal 的 DataAnnotationsRuleProvider 和 Microsoft 的 DataAnnotationsModelBinder(以及 Martijn 的 cmets)复制模式,为 MVC 1.0 编写了自己的 ValidationService。服务接口如下:

public interface IValidationService

    void Validate(object instance);

    IEnumerable<ErrorInfo> GetErrors(object instance);


public abstract class BaseValidationService : IValidationService

    public void Validate(object instance)
    
        var errors = GetErrors(instance);

        if (errors.Any())
            throw new RulesException(errors);
    

    public abstract IEnumerable<ErrorInfo> GetErrors(object instance);

该服务是一个验证运行程序,它遍历它接收到的对象实例的属性树,并实际执行它在每个属性上找到的验证属性,当属性无效时构建一个 ErrorInfo 对象列表。 (我会发布整个源代码,但它是为客户编写的,我还不知道我是否有权这样做。)

然后,您可以让您的控制器、业务逻辑服务在您准备好时显式调用验证,而不是完全依赖模型绑定器进行验证。

您还应该注意其他几个陷阱:

数据中的默认DataTypeAttribute 注释实际上并没有做任何事情 数据类型验证,所以你需要 写一个新的属性 实际上使用 xVal 正则 表达式(或其他东西) 执行服务器端数据类型 验证。 xVal 无法行走 创建客户端的属性 验证,所以你可能想要 那里进行了一些更改以变得更强大 客户端验证。

如果我被允许并且有时间,我会尝试提供更多资源......

【讨论】:

【参考方案5】:

见codeProjectServer-side Input Validation using Data Annotations

输入验证可以在客户端自动完成 ASP.NET MVC 或根据规则显式验证模型。这 提示将描述如何在服务器端手动完成 ASP.NET 应用程序或 WPF 的存储库代码中 应用程序。

        // Use the ValidationContext to validate the Product model against the product data annotations
        // before saving it to the database
        var validationContext = new ValidationContext(productViewModel, serviceProvider: null, items:null);
        var validationResults = new List<ValidationResult>();

        var isValid = Validator.TryValidateObject(productViewModel, validationContext,validationResults, true);

【讨论】:

以上是关于ASP.NET MVC:数据注释验证是不是足够?的主要内容,如果未能解决你的问题,请参考以下文章

数据注释在部分 ASP.NET MVC 中不起作用 jquery 不显眼的验证

ASP.NET MVC:通过 DataAnnotation 进行自定义验证

ASP.NET MVC 4 模型验证

未验证 ASP.NET MVC5 模型

ASP.NET MVC:窗体身份验证及角色权限管理示例

如何在 ASP.Net MVC Identity 2 中更改密码验证?