ASP.NET MVC 模型绑定和验证顺序

Pau*_*ing 5 validation asp.net-mvc model-binding model-validation asp.net-mvc-4

我有一个模型(简化)如下:

public UserModel {
    ...
    public USState State {get; set; }
    public string StateString {get; set; }
    public Country Country {get; set; }
    ...
}
Run Code Online (Sandbox Code Playgroud)

我需要的验证规则是:

  1. 如果Country美国,State需要。
  2. 如果Country不是美国,StateString需要。

我创建了一个自定义验证属性RequiredIfAttribute。这很好用,所以我不打算用它的实现来回答这个问题。它有三个必需的成员:

  1. CompareField - 这是用于检查是否需要验证的字段。
  2. CompareValue - 这是它将用来比较以决定是否需要验证的值。
  3. CompareType - 这就是它如何比较值来决定是否需要验证。

所以有了这个,我更新了我的模型:

public UserModel {
    ...
    [RequiredIf("Country", Country.USA, EqualityType.Equals)]    
    public USState State {get; set; }
    [RequiredIf("Country", Country.USA, EqualityType.NotEquals)] 
    public string StateString {get; set; }
    [Required]                                                   
    public Country Country {get; set; }
    ...
}
Run Code Online (Sandbox Code Playgroud)

我应该在这里注意,我的RequiredIfAttribute也有客户端验证。这完美地工作。

现在来解决这个问题...

我发布以下值:

州 = AL
StateString = null
国家 =美国

这符合我的验证规则,应该是有效的。这是但是ModelState告诉我它无效。显然 StateString 字段是必需的。那不是我指定的。为什么我的验证规则没有按预期应用?

(如果您知道此时出了什么问题,那么就不必阅读问题的其余部分)

这就是正在发生的事情。在RequiredIfAttribute被触发三次。但是等等,我只使用它两次。它正在像这样被触发:

  1. 触发StateString(返回无效
  2. 触发State(这返回有效
  3. 触发StateString(这返回有效

这很奇怪。它验证StateString两次,第一次通过,第二次失败。情节变厚了...

我进一步研究了这一点,发现它第一次尝试验证StateStringCountry 没有设置。第二次尝试验证时StateStringCountry 设置。仔细观察,似乎第一次尝试验证StateString发生在我的模型完全绑定之前。下面StateString(在代码中)的所有属性(未在示例模型中列出)均未绑定。第二次尝试验证StateString,所有属性都被绑定。

我已经解决了这个问题,但我对它没有信心,因为我根本不信任它。为了让我的验证按预期工作,我重新安排了模型(为简洁起见,删除了属性):

public UserModel {
    ...
    public Country Country {get; set; }
    public USState State {get; set; }
    public string StateString {get; set; }
    ...
}
Run Code Online (Sandbox Code Playgroud)

RequiredIfAttribute仍然触发三次同上,但ModelState告诉我,该发布的数据(如上)现在是有效的,像变魔术一样!

我看到的是这个(我的假设):

1. Start binding (property by property, top to bottom in code (risky))
2. Arrive at `StateString` and decide to try and validate
3. Finish binding
4. Validate all properties
Run Code Online (Sandbox Code Playgroud)

我真的有两个问题:
1. 为什么会出现这种行为?
2. 我怎样才能阻止这种行为?

Mat*_*sch 1

模型绑定过程中存在大量错综复杂的问题。复杂的模型将得到全面的重新验证。

我建议为了更好地理解该过程,您可以深入研究源代码以了解到底发生了什么。

http://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Http/ModelBinding/Binders/MutableObjectModelBinder.cs

有一个后处理阶段:

// post-processing, e.g. property setters and hooking up validation
ProcessDto(actionContext, bindingContext, dto);
bindingContext.ValidationNode.ValidateAllProperties = true; // complex models require full validation
Run Code Online (Sandbox Code Playgroud)

有一个预处理阶段:

// 'Required' validators need to run first so that we can provide useful error messages if
// the property setters throw, e.g. if we're setting entity keys to null. See comments in
// DefaultModelBinder.SetProperty() for more information.
Run Code Online (Sandbox Code Playgroud)

除了实现您自己的模型绑定器之外,似乎没有很多方法可以影响这一点。