MVC验证 - 使用服务层保持干燥 - 最佳做法是什么?

问题描述 投票:15回答:6

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

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

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

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

c# asp.net-mvc asp.net-mvc-3 validation model-view-controller
6个回答
17
投票

我知道,似乎做MVC验证违反DRY,但实际上......它不会......至少不适用于大多数(非平凡的)应用程序。

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

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

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


6
投票

这就是我做到的。

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

然后在Exception上创建一个扩展方法,它将错误的细节复制到ModelState(我从Steve Sandersons那里获得了相当优秀的'Pro Asp.Net MVC 2 Framework'一书) - 如果你这样做,MVC将突出显示无效字段,在UI等中显示错误

然后你的控制器将包含这样的东西

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

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

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

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

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

有一个很好的库叫做AutoMapper ,它可以很容易地从你的域对象映射到你的DTO(反之亦然),而不需要很多样板代码。


4
投票

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

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

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

  2. 如果您使用的是WCF,则使用故障合同和例外。

我的典型MVC应用程序解决方案如下所示:

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

祝好运!


2
投票

这里的问题是服务层验证,但如何将这些信息“备份”到Web应用程序。 我们稍后讨论了类似的事情,因为如果服务正在验证,依赖注入的想法在这里明显起作用,你不能在模型中调用服务(例如,如果在那里实现了IValidateableObject,你不想调用服务直)

采取的方法是:

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

ASP.NET MVC 3:在需要模型外部信息时验证模型

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


1
投票

斯蒂芬建议在这种情况下是完美的。 目前,我正在研究一个非常大的MVC 3.0应用程序,其中包含SOA和其他内容。 因此,在回复中,您希望填写所有必要的信息并将其显示给您的视图(当然控制器将指示)。 希望这可以帮助。


0
投票

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

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

您可能能够利用IValidateableObject接口,但这再次将您与某些特定于ASP.net的东西联系在一起。 也许折衷方案是使用您的响应对象并转换为特定于DataAnnotation的错误。

© www.soinside.com 2019 - 2024. All rights reserved.