【问题标题】:Can I add a ModelState.AddModelError from a Model class (rather than the controller)?我可以从模型类(而不是控制器)添加 ModelState.AddModelError 吗?
【发布时间】:2011-09-19 22:22:59
【问题描述】:

我想使用 ModelState.AddModelError() 在 ASP.MVC 3 输入表单中向用户显示错误,以便它自动突出显示正确的字段并将错误放在特定字段旁边。

在大多数示例中,我看到 ModelState.AddModelError() 和 if(ModelState.IsValid) 直接放在控制器中。但是,我想将该验证逻辑移动/集中到模型类中。我可以让模型类检查模型错误并填充 ModelState.AddModelError() 吗?

当前代码:

// Controller
[HttpPost]
public ActionResult Foo(Bar bar)
{  
    // This model check is run here inside the controller.
    if (bar.isOutsideServiceArea())
        ModelState.AddModelError("Address", "Unfortunately, we cannot serve your address.");

    // This is another model check run here inside the controller.
    if (bar.isDuplicate())
        ModelState.AddModelError("OrderNumber", "This appears to be a duplicate order");

    if (ModelState.IsValid)
    {
        bar.Save();
        return RedirectToAction("Index");
    }
    else
        return View(bar)
}

所需代码:

// Controller
[HttpPost]
public ActionResult Foo(Bar bar)
{  
    // something here to invoke all tests on bar within the model class

    if (ModelState.IsValid)
    {
        bar.Save();
        return RedirectToAction("Index");
    }
    else
        return View(bar)
}


...
// Inside the relevant Model class
if (bar.isOutsideServiceArea())
    ModelState.AddModelError("Address", "Unfortunately, we cannot serve your address.");

if (bar.isDuplicate())
    ModelState.AddModelError("OrderNumber", "This appears to be a duplicate order");

【问题讨论】:

  • 您想要做的不仅仅是像RequiredAttribute 这样的通用DataAnnotations 对吗?
  • 其实我用的DataAnnotations还是蛮多的,但是有些错误必须在运行时计算。我们的通用模型有一些在数据库中配置和存储并在运行时应用的变体。
  • 到目前为止,我只能通过从控制器调用该方法或在模型中使用 DataAnnotations 使其工作。还没弄清楚如何在模型类中运行一些逻辑并调用 AddModelError()

标签: asp.net-mvc


【解决方案1】:

您可以像 this 那样使用自定义数据注释或像在 NerdDinner 示例中那样使用 RuleViolations。

【讨论】:

  • 哇,我认为这是目前为止的方法,但代码要多得多……不确定是否值得增加复杂性。必须有一种更简单的方法来向模型状态添加错误
  • 就像 Charlino 所说,如果您使用 MVC 3,您可以使用 IValidatableObject(接近于 NerdDinner 所做的,但您不必手动将错误添加到控制器中的模型状态),但除此之外,这就是我所看到的。我个人更喜欢使用自定义数据注释,以便它们可以重复使用,并且您只需使用属性装饰模型属性。
  • 我在设计时可以在任何地方使用 DataAnnotations。但是,有一些检查,例如 isUnique(针对其他记录)必须在运行时完成。所以我要一个组合。谢谢。
【解决方案2】:

也许您可以创建一个像 IErrorHandler 这样的错误接口,并将其传递给您模型类的一个名为 Validate 的公共方法,假设它是一个分部类,您可以将您的数据模型与您的规则分开。

使用该接口,您可以在控制器中创建一个包含 ModelState 错误处理程序的类。所以接口可能有 AddError 并且在那个方法中你只是委托给你的本地模型状态。

所以你的方法可能是这样的:

IErrorHandler errorHandler = CreateErrorHandler();
model.Validate(errorHandler);

if(errorHandler.IsValid())
... do something

【讨论】:

    【解决方案3】:

    如果您使用的是 MVC 3,则应查看 IValidatableObject,这就是您所追求的。

    Scott Gu 在他的 MVC3 Intro 博客文章中提到了这一点。

    【讨论】:

    • 我想这就是我要找的!我现在正在测试它。
    • 仅供参考:中间链接现在返回 404。
    【解决方案4】:

    您可以通过模型上的 IValidatableObject 接口使用自定义验证。

    Custom validation example

    【讨论】:

    • 链接已不存在。
    • 感谢@Storm。这是另一个演示 IValidatableObject 的帖子。 IValidatableObject
    【解决方案5】:

    如果您追求最少的代码量,那么一种巧妙的做法是将该消息从您的View Model 填充到Session,然后从您的Controller 添加该消息,例如:

    // In Model (or elsewhere deep inside your app):
    string propertyName = "Email";
    string errorMessage = "This is the error message, you should fix it";
    
    Session["DeepModelError"] = new KeyValuePair<string, string>(propertyName, errorMessage);
    
    // In Controller
    var model = new LoginModel();
    if (Session["DeepModelError"] != null)
    {
        var deepError = (KeyValuePair<string, string>)Session["DeepModelError"];
        if (deepError.Key == "Email" || deepError.Key == "Password")
        {
            ModelState.AddModelError(deepError.Key, deepError.Value);
    
            Session["DeepModelError"] = null;
        }
    }
    

    让我重申一下,我意识到这有点 hacky,但它很简单并且可以完成工作......

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-25
      • 2010-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多