MVC验证—使用服务层保持其干燥—最佳实践是什么?

时间:2022-02-16 20:07:07

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.

我试图坚持最佳的多层设计实践,并且不希望我的MVC控制器与我的DAL(或者任何IRepository)交互。它必须通过我的业务服务层来执行适当的业务规则和验证。验证——我不希望在控制器中使用域模型实体上的各种验证属性(如[Required])执行验证,因为这将使我的前端更清晰。更不用说这个服务也可以通过WPF前端实现。

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)

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

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?

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

6 个解决方案

#1


17  

I know, it seems like doing MVC validation violates DRY, but in reality.. it doesn't.. at least not for most (non-trivial) applications.

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

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.

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

The view is validation of the input, not validation of the model.

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

#2


6  

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.

然后在Exception上创建一个扩展方法,该方法将错误的细节复制到ModelState(我是从Steve Sandersons相当优秀的“Pro Asp”中得到这个想法的。如果你做对了,MVC会高亮显示无效的字段,显示UI中的错误等等。

Then your controller will contain something like this

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

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

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.

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

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.

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

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.

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

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.

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

#3


4  

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.

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

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:

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

  1. 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.

    返回XXXResult对象,而不是void或质数。如果您的服务方法是AddProduct,那么返回addproductserviceoperationresult。此结果包含成功/失败指示器以及其他信息。

  2. If you are using WCF then use Fault Contracts and exceptions.

    如果您正在使用WCF,那么请使用错误契约和异常。

A typical MVC Application solution of mine looks like this:

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

  • MVC Website Project
  • MVC网站项目
  • xxx.Model (project, referenced by most layers)
  • xxx。模型(项目,被大多数层引用)
  • xxx.Services (project)
  • xxx。服务(项目)
  • xxx.DataAccess (project, sometimes merge with services)
  • xxx。DataAccess(项目,有时与服务合并)
  • others as needed
  • 根据需要别人

Good luck!

好运!

#4


2  

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)

这里的问题是验证服务层,但如何获得这些信息“备份”web应用程序,我们讨论了一些类似以来依赖注入的想法在这里显然如果验证服务,你不能调用服务的崭新模型中(例如如果IValidateableObject实现你不想直接调用服务)

The approach taken was:

采取的方法是:

Option 3: I didn't know about this earlier, but what appears to be a very powerful way to write validators is to use the ModelValidator class and a corresponding ModelValidatorProvider.

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

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

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

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.

因此,基本上您是在不需要显式的服务定位器调用的情况下,向mvc解析一个验证器(然后在您的服务层中)。

#5


1  

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.

在这种情况下,史蒂芬的建议是完美的。目前,我正在开发一个包含SOA和其他内容的大型MVC 3.0应用程序。因此,在响应中,您希望填充所有必要的信息并将它们显示给您的视图(当然,控制器将口述)。希望这个有帮助。

#6


0  

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.

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

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.

您可能可以利用IValidateableObject接口,但是这又将您与特定于asp.net的内容联系在一起。也许折衷的办法是使用响应对象并将其转换为特定于dataannotations.com的错误。

#1


17  

I know, it seems like doing MVC validation violates DRY, but in reality.. it doesn't.. at least not for most (non-trivial) applications.

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

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.

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

The view is validation of the input, not validation of the model.

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

#2


6  

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.

然后在Exception上创建一个扩展方法,该方法将错误的细节复制到ModelState(我是从Steve Sandersons相当优秀的“Pro Asp”中得到这个想法的。如果你做对了,MVC会高亮显示无效的字段,显示UI中的错误等等。

Then your controller will contain something like this

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

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

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.

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

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.

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

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.

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

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.

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

#3


4  

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.

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

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:

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

  1. 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.

    返回XXXResult对象,而不是void或质数。如果您的服务方法是AddProduct,那么返回addproductserviceoperationresult。此结果包含成功/失败指示器以及其他信息。

  2. If you are using WCF then use Fault Contracts and exceptions.

    如果您正在使用WCF,那么请使用错误契约和异常。

A typical MVC Application solution of mine looks like this:

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

  • MVC Website Project
  • MVC网站项目
  • xxx.Model (project, referenced by most layers)
  • xxx。模型(项目,被大多数层引用)
  • xxx.Services (project)
  • xxx。服务(项目)
  • xxx.DataAccess (project, sometimes merge with services)
  • xxx。DataAccess(项目,有时与服务合并)
  • others as needed
  • 根据需要别人

Good luck!

好运!

#4


2  

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)

这里的问题是验证服务层,但如何获得这些信息“备份”web应用程序,我们讨论了一些类似以来依赖注入的想法在这里显然如果验证服务,你不能调用服务的崭新模型中(例如如果IValidateableObject实现你不想直接调用服务)

The approach taken was:

采取的方法是:

Option 3: I didn't know about this earlier, but what appears to be a very powerful way to write validators is to use the ModelValidator class and a corresponding ModelValidatorProvider.

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

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

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

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.

因此,基本上您是在不需要显式的服务定位器调用的情况下,向mvc解析一个验证器(然后在您的服务层中)。

#5


1  

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.

在这种情况下,史蒂芬的建议是完美的。目前,我正在开发一个包含SOA和其他内容的大型MVC 3.0应用程序。因此,在响应中,您希望填充所有必要的信息并将它们显示给您的视图(当然,控制器将口述)。希望这个有帮助。

#6


0  

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.

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

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.

您可能可以利用IValidateableObject接口,但是这又将您与特定于asp.net的内容联系在一起。也许折衷的办法是使用响应对象并将其转换为特定于dataannotations.com的错误。