企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持知识库和私有化部署方案 广告
## 4.3 ABP应用层 - 数据传输对象验证 应用程序的输入首先应该被验证是否有效。输入的数据能够被用户或者其它应用发送。在web应用中,验证通常被实现两次:客户端和服务器端。客户端验证的实现主要用于用户体验。首先,最好是在客户端检验表单并且向用户展示无效的字段。但是,相对于客户端验证,服务器端验证是更重要并且不可缺失的。 服务器端的验证通常是在应用层或者控制器中被实现(通常,所有的服务都是从持久层获取数据)。应用服务中的方法首先应检测输入数据(有效性)然后使用这些数据。ABP为所有应用的输入数据提供了高效的基础设施来实现自动验证;如: + 所有应用服务的方法 + 所有ASP.NET Core MVC Controlle Actions + 所有ASP.NET MVC 和 Web API Controller Actions 如果需要禁用验证,请查看禁用验证章节。 ### 4.3.1 使用数据注解 ABP可以使用数据注解来检验数据的有效性。假设我们正在开发一个创建任务的应用服务,并且得到了一个输入,如下所示: ```csharp public class CreateTaskInput { public int? AssignedPersonId { get; set; } [Required] public string Description { get; set; } } ``` 在这里 Description 属性被标记为 Required。AssignedPersonId 是可选的。在 System.ComponentModel.DataAnnotations 命名空间中,还有很多这样的特性 ( 例如: MaxLength, MinLength, RegularExpression 等等 )。如下所示: ```csharp public class TaskAppService : ITaskAppService { private readonly ITaskRepository _taskRepository; private readonly IPersonRepository _personRepository; public TaskAppService(ITaskRepository taskRepository, IPersonRepository personRepository) { _taskRepository = taskRepository; _personRepository = personRepository; } public void CreateTask(CreateTaskInput input) { var task = new Task { Description = input.Description }; if (input.AssignedPersonId.HasValue) { task.AssignedPerson = _personRepository.Load(input.AssignedPersonId.Value); } _taskRepository.Insert(task); } } ``` 正如你所看到的,这里没有写任何的数据验证代码,因为ABP会自动去验证数据。ABP也会检验输入数据是否为null。如果为空则会抛出AbpValidationException 异常。所以你不需要写检验数据是否为null值的代码。如果有任何属性的输入数据是无效的它也会抛出相同的异常。 这个机制近似于 ASP.NET MVC 的验证功能,注意:这里的应用服务类不是继承自Controller,它是用在Web应用的一个普通类,即使不是一个WEB应用它也能够正常工作。 ### 4.3.2 自定义检验 如果数据注解的方式不能满足你的需求,你可以实现ICustomValidate接口,请看下面示例: ```csharp public class CreateTaskInput : ICustomValidate { public int? AssignedPersonId { get; set; } public bool SendEmailToAssignedPerson { get; set; } [Required] public string Description { get; set; } public void AddValidationErrors(CustomValidatationContext context) { if (SendEmailToAssignedPerson && (!AssignedPersonId.HasValue || AssignedPersonId.Value <= 0)) { context.Results.Add(new ValidationResult("AssignedPersonId must be set if SendEmailToAssignedPerson is true!")); } } } ``` **ICustomValidate** 接口声明了一个可被实现的 **AddValidationErrors** 方法。如果有验证错误的话,我们必须添加 **ValidationResult** 对象到 **context.Results** 列表。如果需要的话,你也可以使用 **context.IocResolver** 来解决依赖关系。 除了 **ICustomValidate** 接口,ABP也对.NET标准的 **IValidatableObject** 接口提供支持。你也可以实现它来执行自定义验证。如果你实现了这两个接口,那么这两个接口的方法都会被调用。 ### 4.3.3 禁用验证 对于自动验证类,你可以使用这些特性来控制验证: + **DisableValidation** 特性能够被用来对DTO的类,方法或者属性来禁用验证 + **EnableValidation** 特性仅能够被用来开启都某个方法的验证;如果对某个类禁用验证,但是想对该类的某个方法开启验证 ### 4.3.4 标准化 在验证数据输入后,我们需要执行一个额外的操作来组织DTO参数。ABP定义了一个 **IShouldNormalize** 接口,这个接口声明了一个 **Normalize** 的方法。如果你实现了这个接口,在数据验证后,**Normalize** 方法就会被调用。假设我们的DTO需要一个排序方向的属性。如果这个Sorting属性没有值,但是我们想要设置一个默认值;如下所示: ```csharp public class GetTasksInput : IShouldNormalize { public string Sorting { get; set; } public void Normalize() { if (string.IsNullOrWhiteSpace(Sorting)) { Sorting = "Name ASC"; } } } ```