ABP应用层 - 数据传输对象验证
4.3 ABP应用层 - 数据传输对象验证
应用程序的输入首先应该被验证是否有效。输入的数据能够被用户或者其它应用发送。在web应用中,验证通常被实现两次:客户端和服务器端。客户端验证的实现主要用于用户体验。首先,最好是在客户端检验表单并且向用户展示无效的字段。但是,相对于客户端验证,服务器端验证是更重要并且不可缺失的。
服务器端的验证通常是在应用层或者控制器中被实现(通常,所有的服务都是从持久层获取数据)。应用服务中的方法首先应检测输入数据(有效性)然后使用这些数据。ABP为所有应用的输入数据提供了高效的基础设施来实现自动验证;如:
所有应用服务的方法
所有ASP.NET Core MVC Controlle Actions
所有ASP.NET MVC 和 Web API Controller Actions
如果需要禁用验证,请查看禁用验证章节。
4.3.1 使用数据注解
ABP可以使用数据注解来检验数据的有效性。假设我们正在开发一个创建任务的应用服务,并且得到了一个输入,如下所示:
public class CreateTaskInput
{
public int? AssignedPersonId { get; set; }
[Required]
public string Description { get; set; }
}
在这里 Description 属性被标记为 Required。AssignedPersonId 是可选的。在 System.ComponentModel.DataAnnotations 命名空间中,还有很多这样的特性 ( 例如: MaxLength, MinLength, RegularExpression 等等 )。如下所示:
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接口,请看下面示例:
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属性没有值,但是我们想要设置一个默认值;如下所示:
public class GetTasksInput : IShouldNormalize
{
public string Sorting { get; set; }
public void Normalize()
{
if (string.IsNullOrWhiteSpace(Sorting))
{
Sorting = "Name ASC";
}
}
}