MVC 验证 - 通过服务层保持干燥 - 最佳实践是什么?
我试图遵循最佳的多层设计实践,并且不希望我的 MVC 控制器与我的 DAL(或任何与此相关的 IRepository)交互。它必须通过我的业务服务层来执行适当的业务规则和验证。验证 - 我不想在控制器中使用域模型实体上的各种验证属性(例如 [Required])执行验证,因为这可以照亮我的前端。更何况这个服务还可以通过WPF前端来实现。
由于我的验证是在服务层中完成的,因此将值返回到 UI 的最佳实践是什么?我不需要“void addWhatever(int somesID)”,因为我需要知道它是否失败。它应该是一个布尔值吗?它应该是一个枚举吗?我应该利用异常处理吗?或者我应该返回一些类似于 MVC 在将验证属性装饰到 Model 对象时使用的 IValidationDictionary 对象吗? (如果需要,我可以稍后在 UI 中使用适配器模式)
我想将我的实体从控制器传递到服务层,并了解验证/数据持久性是否失败。我也不想忽视这样一个事实:我需要返回一个视图,指示可能验证失败的每个字段的正确错误消息(我希望尽可能保持轻松)。
我有过几个想法,但都感觉不太对劲。我觉得答案包括特定于视图的模型实体,但这会导致必须处理整个映射问题,更不用说这违反了 DRY(不要重复自己)原则。什么是最佳实践?
I am trying to adhere to best multi-layer design practices, and don't want my MVC controller to interact with my DAL (or any IRepository for that matter). It must go through my business service layer to enforce proper business rules and validation. Validation - I don't want to perform validation in the controller using the various validation attributes ( such as [Required]) on my domain model entities because this sheds light on my front end. Not to mention this service can also be implemented through a WPF front end.
Since my validation is being done in my service layer, what are best practices for returning values back to the UI? I don't want a 'void addWhatever(int somethingsID)', because I need to know if it failed. Should it be a boolean? Should it be a Enum? Should I take advantage of exception handling? Or should I return some IValidationDictionary object similar to that used by MVC when adorning validation attributes to Model objects? (which I could use an adapter pattern in the UI later if needs be)
I would like to pass my entity from the controller to the service layer, and understand whether or not validation/data-persistence failed. I also don't want to lose sight on the fact that I need to return a view indicating the proper error messages for each field that may have failed validation (I'd like to keep this as painless as possible).
I have had several ideas, all of which don't feel right. I feel the answer includes View-specific-model entities, but this leads to a whole mapping issue that must be dealt with, not to mention this violates the DRY (Don't repeat yourself) principle. What is best practice?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我知道,执行 MVC 验证似乎违反了 DRY,但实际上……它没有……至少对于大多数(重要的)应用程序来说不是。
为什么?因为视图的验证要求通常与业务对象的验证要求不同。您的视图验证涉及验证特定视图是否有效,而不是您的业务模型是否有效。
有时这两者是相同的,但如果您构建应用程序时视图要求业务模型有效,那么您就会将自己锁定在这种情况下。如果您需要将对象创建拆分为两个页面会发生什么?如果您决定将服务层用于 Web 服务,会发生什么?通过将您的 UI 锁定到业务层验证场景中,您会严重削弱您可以提供的解决方案类型。
视图是输入的验证,而不是模型的验证。
I know, it seems like doing MVC validation violates DRY, but in reality.. it doesn't.. at least not for most (non-trivial) applications.
Why? Because your view's validation requirements are quite often different from your business objects validation requirements. Your views validation concerns itself with validating that a specific view is valid, not that your business model is valid.
Sometimes those two are the same, but if you build your app so that the view requires the business model to be valid, then you are locking yourself into this scenario. What happens if you need to split object creation into two pages? What happens if you decide to use the service layer for a web service? By locking your UI into a business layer validation scenario you severly cripple the kinds of solutions you can provide.
The view is validation of the input, not validation of the model.
我就是这样做的。
让您的服务层在业务规则/验证规则失败时抛出异常。为此创建您自己的验证异常,并包含一些属性来保存验证错误的详细信息 - (例如哪个属性有验证错误,以及消息是什么)
然后在异常上创建一个扩展方法,它将复制错误的详细信息到 ModelState(我从 Steve Sandersons 相当优秀的“Pro Asp.Net MVC 2 Framework”书中得到这个想法) - 如果你做得正确,MVC 将突出显示无效字段,在 UI 中显示错误等。
然后你的控制器将包含一些内容像这样
这意味着您的业务规则和验证现在位于您的服务层中,并且可以重复使用。
还可以考虑将 DTO/视图模型传递到视图,并将域对象映射到 DTO(反之亦然),而不是将域对象传递到视图。
然后,DTO/视图模型可以驻留在 MVC 层中,您可以使用验证属性来装饰它们,并让控制器将它们传递给视图 - 从而使用内置的 MVC 验证。
您会发现,对于任何复杂的项目,您在 UI 端所需的验证可能与您在业务规则端所需的验证略有不同,因此这有助于将其分开。
有一个名为 AutoMapper 的优秀库,它可以轻松地将域对象映射到 DTO(反之亦然) )没有很多样板代码。
This is how I have done it.
Have your service layer throw exceptions on business rule/validation rule failure. Create your own validation Exception for this, and include some properties to hold details of the validation error - (e.g. which property has the validation error, and what the message is)
Then create an extension method on Exception which will copy the details of the error to the ModelState (I got this idea from Steve Sandersons rather excellent 'Pro Asp.Net MVC 2 Framework' book) - if you do this right, MVC will highlight invalid fields, show errors in the UI etc.
Then your controller will contain something like this
This means that your business rules and validation are now in your service layer, and this could be re-used.
Consider also passing DTOs / View Models to your Views, and mapping your domain objects to DTO's and (vice-versa), rather than passing your domain objects to your views.
Then the DTOs / View Models can reside in the MVC layer, and you can decorate these with the Validation attributes, and have the controller pass these to the views - thus using the built in MVC validation.
You will find that for any complex project, the validation you require at the UI end may be slightly different from what you require at the business rules end, so this helps seperate that.
There is a good library called AutoMapper which makes it easy to map from your domain objects to your DTOs (and vice versa) without a lot of boilerplate code.
我建议您通过使用数据注释装饰模型类来利用内置的 MVC 验证。这仅适用于基本输入验证,与处理业务规则和验证不同。数据注释很棒,因为它们对于任何了解它们的消费者都很有用,但不会对不了解如何使用它们的消费者产生不利影响。
我认为您使用服务层来抽象业务规则和数据访问是正确的。您可能需要做一些事情来增强控制器和服务之间的交互:
返回 XXXResult 对象而不是 void 或基元。如果您的服务方法是 AddProduct,则返回 AddProductResult 或更广泛的 ProductServiceOperationResult。此结果包含成功/失败指示符以及其他信息。
如果您使用 WCF,则使用错误契约和异常。
我的一个典型的MVC应用程序解决方案如下所示:
祝你好运!
I recommend you take advantage of the built in MVC validation by decorating your Model classes with data annotations. This is only for basic input validation which is different from processing business rules and validation. Data Annotations are great because they are useful for any consumer that is aware but don't adversely impact consumers that don't understand how to use them.
I think you are right to use a service layer to abstract business rules and data access. You may want to do a couple of things to enhance the interaction between controller and service:
Return XXXResult objects instead of void or primatives. If your service method is AddProduct then return AddProductResult or more broadly ProductServiceOperationResult. This result contains success/fail indicator as well as additional information.
If you are using WCF then use Fault Contracts and exceptions.
A typical MVC Application solution of mine looks like this:
Good luck!
这里的问题是服务层中的验证,以及如何将该信息“备份”到 Web 应用程序。
我们之前讨论过类似的事情,因为如果服务正在验证,那么依赖注入的想法显然会在这里发挥作用,您不能简单地在模型中调用该服务(例如,如果 IValidateableObject 在那里实现,您不想调用该服务直接)
采取的方法是:
所以基本上,您正在注入一个验证器(然后将在您的服务层中)由 mvc 解析,而不需要显式的服务定位器调用。
The issue here is validation in the service layer but how to get that information 'back up' to the web app.
Something similar we discussed a bit back since the idea of dependency injection clearly comes into play here if a service is validating, you can't sinply call the service in the Model (for instance if IValidateableObject is implemented there you dont want to call the service directly)
The approach taken was:
So basically you are injecting a validator (which would then be in your service layer) to be resolved by mvc without a need to an explicit service locator call.
斯蒂芬的建议在这种情况下是完美的。目前,我正在开发一个非常大的带有SOA的MVC 3.0应用程序,并且涉及其他事情。因此,在响应中,您需要填写所有必要的信息并将其显示给您的视图(当然控制器会指示)。希望这有帮助。
Stephen advice is perfect in this scenario. Currently, I am working on a very large MVC 3.0 application with SOA and other things are involved. So in a response, you would like to fill all the necessary information and showing them to your views (of-course controller will dictate). Hope this helps.
实际上,在几个层(客户端、控制器中的服务器端或同等层,以及业务层中再次运行)重复运行验证并不是一件坏事。它使您的代码在某种程度上脱钩。理想情况下,您只需在一处描述它们,但有时这是不可能的。由于未能使用数据注释,如果您想做客户端验证,您是否会感到非常困难?看来是这样。
不管怎样,我过去在非 mvc 应用程序中所做的就是让大多数操作方法返回一个 Response 对象,其中包括状态(成功、错误、警告)和验证错误列表,以及所需的任何其他属性。
您也许能够利用 IValidateableObject 接口,但这又将您与 ASP.net 特定的东西联系起来。也许一种折衷方案是使用您的响应对象并转换为特定于 DataAnnotation 的错误。
It's actually not a bad thing to repeatedly run validations in a few layers (client side, server side in the controller or equivalent, and again in the business layer). It makes your code somewhat uncoupled. Ideally you'd only have to describe them in one place, but sometimes this is not possible. By failing to use data annotations, are you making it really hard on yourself if you want to do client-side validations? It seems so.
Anyway, what I've done in the past in non-mvc applications is have most action methods return a Response object that includes a status (success, errors, warnings) and a list of validation errors, as well as any other properties required.
You might be able to leverage the IValidateableObject interface, but this again ties you somewhat to something ASP.net-specific. Perhaps a compromise would be to consume your response object and convert to DataAnnotation-specific errors.