项目

验证机制

验证系统用于对特定控制器操作或服务方法的用户输入及客户端请求进行校验。

ABP完全兼容ASP.NET Core的模型验证体系,其官方文档所述内容均适用于基于ABP的应用程序。因此,本文档主要聚焦ABP的特有功能,而非重复微软文档内容。

此外,ABP还提供以下优势:

  • 通过定义IValidationEnabled接口可为任意类启用自动验证。由于所有应用服务默认实现该接口,它们也会自动获得验证能力。
  • 自动对数据注解属性的验证错误进行本地化处理。
  • 提供可扩展服务来验证方法调用或对象状态。
  • 集成FluentValidation支持。

DTO验证

本节简要介绍验证系统,详见ASP.NET Core验证文档

数据注解属性

使用数据注解是以声明方式为DTO实现形式化验证的简便方法。示例:

public class CreateBookDto
{
    [Required(ErrorMessage = "名称是必填项")]
    [StringLength(100)]
    public string Name { get; set; }

    [Required(ErrorMessage = "描述是必填项")]
    [StringLength(1000)]
    public string Description { get; set; }

    [Range(0, 999.99)]
    public decimal Price { get; set; }
}

当您在应用服务或控制器中将此类作为参数使用时,系统会自动进行验证并抛出本地化的验证异常(由ABP统一处理)。

IValidatableObject接口

DTO可实现IValidatableObject接口以执行自定义验证逻辑。下例中的CreateBookDto实现该接口,检查Name是否与Description相同,并在这种情况下返回验证错误。

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace Acme.BookStore
{
    public class CreateBookDto : IValidatableObject
    {
        [Required]
        [StringLength(100)]
        public string Name { get; set; }

        [Required]
        [StringLength(1000)]
        public string Description { get; set; }

        [Range(0, 999.99)]
        public decimal Price { get; set; }

        public IEnumerable<ValidationResult> Validate(
            ValidationContext validationContext)
        {
            if (Name == Description)
            {
                yield return new ValidationResult(
                    "名称和描述不能相同!",
                    new[] { nameof(Name), nameof(Description) }
                );
            }
        }
    }
}

服务解析

若需从依赖注入系统解析服务,可通过ValidationContext对象实现。示例:

var myService = validationContext.GetRequiredService<IMyService>();

虽然在Validate方法中解析服务提供了极大灵活性,但将领域验证逻辑置于DTO中并非最佳实践。应保持DTO简洁性,其核心职责是数据传输(DTO:数据传输对象)。

验证基础设施

本节详解ABP提供的附加服务。

IValidationEnabled接口

IValidationEnabled是一个空标记接口,任何类(注册并解析自DI)实现此接口后,ABP将对该类方法启用验证系统。示例:

using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Validation;

namespace Acme.BookStore
{
    public class MyService : ITransientDependency, IValidationEnabled
    {
        public virtual async Task DoItAsync(MyInput input)
        {
            //...
        }
    }
}

ABP使用动态代理/拦截器系统执行验证。为使该机制生效,您的方法应声明为virtual,或服务应通过接口注入使用(如IMyService)。

启用/禁用验证

可使用[DisableValidation]特性为方法、类或属性禁用验证。

[DisableValidation]
public void MyMethod()
{
}

[DisableValidation]
public class InputClass
{
    public string MyProperty { get; set; }
}

public class InputClass
{
    [DisableValidation]
    public string MyProperty { get; set; }
}

AbpValidationException

当ABP检测到验证错误时,会抛出类型为AbpValidationException的异常。应用程序代码也可抛出此异常,但通常无需手动处理。

  • AbpValidationExceptionValidationErrors属性包含验证错误列表。
  • 该异常的日志级别设置为Warning,会将所有验证错误记录到日志系统
  • ABP会自动捕获AbpValidationException并将其转换为包含HTTP 400状态码的友好错误格式。详见异常处理文档。

高级主题

IObjectValidator服务

除自动验证外,您可能需要手动验证对象。此时可通过依赖注入使用IObjectValidator服务:

  • ValidateAsync方法根据验证规则校验给定对象,无效时抛出AbpValidationException
  • GetErrorsAsync不抛出异常,仅返回验证错误。

IObjectValidator默认由ObjectValidator实现。该验证器具有可扩展性:通过实现IObjectValidationContributor接口可加入自定义逻辑。示例:

public class MyObjectValidationContributor
    : IObjectValidationContributor, ITransientDependency
{
    public Task AddErrorsAsync(ObjectValidationContext context)
    {
        //获取正在验证的对象
        var obj = context.ValidatingObject;

        //添加验证错误(如存在)
        context.Errors.Add(...);
        return Task.CompletedTask;
    }
}
  • 记得将类注册到DI(如示例中实现ITransientDependency即可自动注册)。
  • ABP将自动发现该类,并应用于所有类型的对象验证(包括自动方法调用验证)。

IMethodInvocationValidator

IMethodInvocationValidator用于验证方法调用,其内部使用IObjectValidator验证方法参数。通常无需直接使用此服务(框架已自动集成),但在特殊场景中可重写或复用其功能。

FluentValidation集成

Volo.Abp.FluentValidation包通过实现IObjectValidationContributor接口,将FluentValidation库集成到验证系统中。详见FluentValidation集成文档

在本文档中