【问题标题】:MVC Validation - Keep it DRY with a service layer - What is best practice?MVC 验证 - 使用服务层保持 DRY - 最佳实践是什么?
【发布时间】:2011-12-25 20:23:23
【问题描述】:

我正在尝试遵守最佳的多层设计实践,并且不希望我的 MVC 控制器与我的 DAL(或任何 IRepository)进行交互。它必须通过我的业务服务层来执行正确的业务规则和验证。验证 - 我不想在我的域模型实体上使用各种验证属性(例如 [必需])在控制器中执行验证,因为这会照亮我的前端。更不用说这个服务也可以通过 WPF 前端来实现。

由于我的验证是在我的服务层中完成的,将值返回到 UI 的最佳做法是什么?我不想要'void addWhatever(int somethingsID)',因为我需要知道它是否失败。它应该是一个布尔值吗?它应该是一个枚举吗?我应该利用异常处理吗?或者我应该在将验证属性装饰到模型对象时返回一些类似于 MVC 使用的 IValidationDictionary 对象? (如果需要,我可以稍后在 UI 中使用适配器模式)

我想将我的实体从控制器传递到服务层,并了解验证/数据持久性是否失败。我也不想忽略这样一个事实,即我需要返回一个视图,以指示每个可能未通过验证的字段的正确错误消息(我希望尽可能轻松地做到这一点)。

我有几个想法,但都觉得不对劲。我觉得答案包括 View-specific-model 实体,但这会导致必须处理的整个映射问题,更不用说这违反了 DRY(不要重复自己)原则。最佳做法是什么?

【问题讨论】:

    标签: c# asp.net-mvc asp.net-mvc-3 validation model-view-controller


    【解决方案1】:

    这里的问题是服务层中的验证,以及如何将该信息“备份”到 Web 应用程序。 我们之前讨论过类似的东西,因为如果服务正在验证,依赖注入的想法在这里很明显会发挥作用,你不能在模型中简单地调用服务(例如,如果在那里实现了 IValidateableObject 你不想调用服务直接)

    采取的方法是:

    选项 3:我之前并不知道,但似乎是 编写验证器的非常强大的方法是使用 ModelValidator 类和相应的 ModelValidatorProvider。

    ASP.NET MVC 3: Validating model when information external to the model is required

    所以基本上你正在注入一个验证器(然后将在你的服务层中)由 mvc 解析,而不需要显式的服务定位器调用。

    【讨论】:

      【解决方案2】:

      斯蒂芬建议在这种情况下是完美的。目前,我正在开发一个非常大的带有 SOA 的 MVC 3.0 应用程序,并且涉及到其他事情。因此,在响应中,您希望填写所有必要的信息并将它们显示给您的视图(当然控制器会指示)。希望这会有所帮助。

      【讨论】:

        【解决方案3】:

        在几个层(客户端、控制器中的服务器端或等效层,以及再次在业务层中)重复运行验证实际上并不是一件坏事。它使您的代码有些解耦。理想情况下,您只需在一个地方描述它们,但有时这是不可能的。由于未能使用数据注释,如果您想进行客户端验证,您是否真的很难做到?好像是这样。

        无论如何,我过去在非 mvc 应用程序中所做的是让大多数操作方法返回一个 Response 对象,其中包括状态(成功、错误、警告)和验证错误列表,以及任何其他所需的属性。

        您也许可以利用 IValidateableObject 接口,但这又将您与 ASP.net 特定的东西联系在一起。也许妥协是使用您的响应对象并转换为特定于 DataAnnotation 的错误。

        【讨论】:

          【解决方案4】:

          我知道,做 MVC 验证似乎违反了 DRY,但实际上.. 它并没有.. 至少对于大多数(非平凡的)应用程序来说不是。

          为什么?因为您的视图的验证要求通常与您的业务对象验证要求不同。您的视图验证涉及验证特定视图是否有效,而不是您的业务模型是否有效。

          有时这两者是相同的,但如果您构建应用程序以使视图要求业务模型有效,那么您将自己锁定在这种情况下。如果您需要将对象创建分成两个页面会发生什么?如果您决定将服务层用于 Web 服务,会发生什么?通过将您的 UI 锁定在业务层验证场景中,您会严重削弱您可以提供的各种解决方案。

          视图是对输入的验证,而不是对模型的验证。

          【讨论】:

          • 尽管这是一个相当古老的问题,但我想对这个主题进行一些澄清。假设我需要验证 username 不应超过 15 个字符并且它应该是唯一的。 15 个字符的限制会发生在控制器中,检查唯一性会发生在服务中吗?
          • @Aquillo - 不,您将在两个位置都进行验证。这个答案一般是关于视图和验证,而不是关于特定项目。 MVC 验证“模型”而不是属性,模型由 0 个或多个属性组成,这些属性可以具有 0 个或多个验证。 MVC 只说“这个模型有效吗”。
          【解决方案5】:

          我建议您通过使用数据注释装饰您的模型类来利用内置的 MVC 验证。这仅适用于与处理业务规则和验证不同的基本输入验证。数据注释很棒,因为它们对任何有意识的消费者都很有用,但不会对不了解如何使用它们的消费者产生不利影响。

          我认为您使用服务层来抽象业务规则和数据访问是正确的。您可能需要做一些事情来增强控制器和服务之间的交互:

          1. 返回 XXXResult 对象而不是 void 或原语。如果您的服务方法是 AddProduct,则返回 AddProductResult 或更广泛的 ProductServiceOperationResult。此结果包含成功/失败指示符以及其他信息。

          2. 如果您使用的是 WCF,请使用错误协定和异常。

          我的一个典型的MVC应用方案是这样的:

          • MVC 网站项目
          • xxx.Model(项目,被大多数层引用)
          • xxx.Services(项目)
          • xxx.DataAccess(项目,有时与服务合并)
          • 其他根据需要

          祝你好运!

          【讨论】:

            【解决方案6】:

            我就是这样做的。

            让您的服务层在业务规则/验证规则失败时抛出异常。为此创建您自己的验证异常,并包含一些属性来保存验证错误的详细信息 - (例如,哪个属性有验证错误,以及消息是什么)

            然后在 Exception 上创建一个扩展方法,它将错误的详细信息复制到 ModelState(我从 Steve Sandersons 相当优秀的“Pro Asp.Net MVC 2 Framework”一书中得到了这个想法)——如果你做对了,MVC将突出显示无效字段,在 UI 中显示错误等。

            那么你的控制器将包含这样的东西

            try
            {
                Service.DoSomeThing();
            }
            catch (Exception err)
            {
                err.CopyTo(ModelState);
            }
            

            这意味着您的业务规则和验证现在在您的服务层中,可以重复使用。

            考虑同时将 DTO / 视图模型传递给您的视图,并将您的域对象映射到 DTO 和(反之亦然),而不是将您的域对象传递给您的视图。

            然后 DTO / 视图模型可以驻留在 MVC 层中,您可以使用 Validation 属性来装饰它们,并让控制器将它们传递给视图 - 从而使用内置的 MVC 验证。

            您会发现,对于任何复杂的项目,您在 UI 端所需的验证可能与您在业务规则端所需的验证略有不同,因此这有助于区分。

            有一个名为AutoMapper 的好库,它可以轻松地从您的域对象映射到您的 DTO(反之亦然),而无需大量样板代码。

            【讨论】:

            • 嗯,我不确定无效数据是否“异常”并且值得例外。为什么选择异常而不是返回结果或使用内置验证接口?
            • 我同意,更重要的是,MVC 层验证未捕获的任何无效数据异常的,并且被服务层捕获。我实际上将 NHibernate.Validator 用于事物的验证方面,这会在数据层中引发异常。我也在 MVC 层中使用内置的验证接口。这个想法是 MVC 层应该使用内置的数据注释捕获输入错误,但是如果无效数据确实通过了 MVC 层,服务层将抛出异常
            • 好吧,这更有意义。就个人而言,我不那样做,但我看到你的澄清有好处。
            • 我喜欢这个,除了它只会通知客户端第一次验证失败。如果请求中有一堆验证失败,客户端必须一次修复一个,并在每次修复后重新发送整个请求。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2010-12-24
            • 2014-01-03
            • 2023-03-12
            • 1970-01-01
            • 2010-09-14
            • 1970-01-01
            • 2017-06-26
            相关资源
            最近更新 更多