服务器端验证

5
我遇到了一些麻烦,无法将服务器端的 DbContext 验证错误传回客户端。我知道Breeze有默认的验证器可以反应出一些属性例如 Required,但其他属性呢?我可以为Breeze编写一个自定义的JavaScript验证器,用于在客户端进行检查,但我还需要检查确保实体在服务器端是有效的。
举个例子,该应用程序要求 Person 具有有效的电子邮件地址。恶意用户会提供一个通过客户端并能提交到服务器的电子邮件地址,但这个数据不会通过 EmailAddress 验证器。迄今为止,我的经验是Breeze将保存电子邮件地址并不弹出任何 DbContext Entity Framework 错误。
假设以下模型,如何获得任何实体验证错误?
public class PeopleContext : DbContext
{
    public PeopleContext()
        : base("name=ConnectionString"){ }

    public DbSet<Person> People { get; set; }
}

public class Person
{
    public int PersonId { get; set; }
    public string FirstName { get; set; }
    [Required]
    public string LastName { get; set; }
    [EmailAddress]
    [Required]
    public string Email { get; set; }
}

更新1:

以下是重现我遇到问题的步骤。

  1. Follow the instructions to create the "Todo" sample (http://www.breezejs.com/documentation/start-nuget)
  2. Add a new custom validator to the BreezeSampleTodoItem.cs file:

    [AttributeUsage(AttributeTargets.Property)]
    public class CustomValidator : ValidationAttribute
    {
        public override Boolean IsValid(Object value)
        {
            string val = (string)value;
            if (!string.IsNullOrEmpty(val) && val == "Error")
            {
                ErrorMessage = "{0} equal the word 'Error'";
                return false;
            }
            return true;
        }
    }
    
  3. Decorate the Description field with the new custom validator:

    [CustomValidator]
    public string Description { get; set; }
    
  4. Add the proper usings of course (System and System.ComponentModel.DataAnnotations).

  5. Run the project.
  6. In one of the description fields type "Error" and save.
这是我期望在Breeze中看到错误提示,或者从Entity Framework抛出DbEntityValidationException。 我已经在两台不同的计算机上尝试过,但结果相同。实体被保存到数据库中,就好像没有错误一样。事实上,如果你在自定义验证器的IsValid方法内部设置断点,你会发现它甚至没有被调用。
2个回答

3

从Breeze v0.78.1开始,所有已注册的DbContext服务器端验证将在EntityManager SaveChanges调用期间执行。遇到任何异常都会导致保存回滚,并将任何验证错误序列化回Breeze客户端。

请注意,此功能尚不支持基于旧的ObjectContext(而不是DbContext)的EF模型。

感谢adamlj发现此问题并提出解决方案。


1

我不确定你的意思是什么

将服务器端的DbContext验证错误返回到客户端

你可能是想要将验证错误消息发送到客户端。但你问题的其余部分表明,你想知道(a)如何在服务器上运行自定义验证和(b)如何获取并运行相应的JavaScript版本的验证。我将解答你的问题。

服务器

Entity Framework(你在例子中使用的)会自动为你运行Data Annotation验证规则...除非你手动禁用了该功能。如果你以正确的方式创建自定义验证规则,EF也会运行这些规则。Daniel Wertheim的这篇文章描述了如何编写这样的规则。我不能保证该文章的每个细节都是正确的,但它对我来说似乎是正确的。它甚至定义了一个自定义的电子邮件验证属性!

如果编写自定义数据注释验证规则对您来说太过复杂(就像对我一样),您可以在 "服务器端拦截" 中讨论的一个 BeforeSave... 方法中编写并调用自己的验证逻辑。

我认为这些是您最好的服务器选项。接下来是客户端...

客户端

Breeze 注册客户端 JavaScript 验证以匹配某些通过元数据传输的服务器端 数据注释(例如,RequiredMaxLength)。目前,自定义 数据注释不被识别或包含在元数据中,并且它们在客户端上没有开箱即用的类似物。如果您希望客户端使用这些规则预筛选实体,则需要编写自己相应的 JavaScript 验证器,并将其注册到相关实体类型,如 验证 文档页面中所述。

如果您有建议或更好的替代方案,我们很乐意听取。


嗨Ward,感谢回复,这个项目真棒!我已经编辑了我的问题,提供了一个复制我遇到的问题的示例。我遇到的问题是,在使用EFContextProvider.SaveChanges(saveBundle)方法时,没有任何数据注释被评估。我确实下载了Breeze的源代码,如果我更改EFContextProvider.SaveChangesCore方法的代码,使用((DbContext)(object)Context).SaveChanges()而不是ObjectContext.SaveChanges(),那么注释将被正确评估。但我宁愿不修改源代码。 - adamlj
还有一点需要注意,我已经检查了BeforeSave方法。我的担忧是我只能返回一个布尔值。对于验证错误,应该有一种方法将错误消息返回给用户。 - adamlj
你应该抛出一个包含失败解释的异常,例如 throw new InvalidOperationException("无法保存未知类型的实体"); 仅返回false告诉ContextProvider在保存期间跳过该实体;它并不能终止保存!详见文档中的 "Server-side interception" 页面。 - Ward
需要明确的是,你应该期望EF在遇到异常时会出现问题。但你不应该期望Breeze在客户端上也会出现问题,因为它对你的CustomValidator一无所知。如果你想在客户端上应用它,你需要创建并注册相应的JS版本。这是次要的问题,与你发现的问题无关。 - Ward
@adamlj - 我很快会投入一些精力来指导异常处理。请看我在这里的评论 - Ward
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接