在ASP.NET Core Web API开发中,模型验证是一个常见但至关重要的环节。标准的数据注解验证虽然强大,但有时我们需要根据特定的业务逻辑来进行自定义验证。本文将详细介绍如何在ASP.NET Core中实现自定义模型验证,并通过一个实际案例展示如何在不重复代码的情况下,在控制器动作执行后进行统一的错误处理。

自定义模型验证的必要性

标准的ASP.NET Core框架提供了许多内置的验证特性,如[Required][StringLength]等,但这些验证常常无法满足复杂业务逻辑的需求。例如,在用户注册时,你可能需要验证用户输入的密码是否符合特定的复杂性要求,或者检查用户名是否已被其他用户占用。

实现自定义验证

假设我们有一个用户注册的API端点,我们希望在用户提交数据后,执行一系列自定义验证逻辑。以下是如何实现这一过程:

  1. 定义模型和自定义验证属性

    public class UserModel
    {
        [Required]
        [EmailAddress]
        public string Email { get; set; }
    
        [Required]
        [CustomPasswordValidation]
        public string Password { get; set; }
    
        // 其他字段...
    }
    
    public class CustomPasswordValidationAttribute : ValidationAttribute
    {
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var password = value as string;
            if (string.IsNullOrEmpty(password))
                return new ValidationResult("密码不能为空");
    
            if (password.Length < 8 || !Regex.IsMatch(password, @"^(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])"))
                return new ValidationResult("密码必须至少8位,且包含大写字母、数字和特殊字符");
    
            return ValidationResult.Success;
        }
    }
    
  2. 在控制器中使用模型

    [ApiController]
    [Route("api/[controller]")]
    public class UserController : ControllerBase
    {
        [HttpPost]
        public IActionResult Register(UserModel user)
        {
            if (!ModelState.IsValid)
            {
                ModelState.AddModelError("CustomError", "验证失败,请检查输入");
            }
    
            // 正常处理逻辑...
    
            return Ok(new { Message = "注册成功" });
        }
    }
    
  3. 使用ActionFilter进行统一错误处理
    为了避免在每个控制器动作中重复编写验证逻辑,我们可以使用ActionFilter来统一处理:

    public class ModelStateValidationFilter : IActionFilter
    {
        public void OnActionExecuted(ActionExecutedContext context)
        {
            if (!context.ModelState.IsValid)
            {
                var errors = context.ModelState
                    .Where(e => e.Value.Errors.Count > 0)
                    .SelectMany(e => e.Value.Errors)
                    .Select(e => e.ErrorMessage)
                    .ToList();
    
                var errorResponse = new
                {
                    Message = "Validation failed",
                    Errors = errors
                };
    
                context.Result = new BadRequestObjectResult(errorResponse);
            }
        }
    
        public void OnActionExecuting(ActionExecutingContext context) { }
    }
    

    然后在Startup.csProgram.cs中添加此过滤器:

    services.AddControllers(options =>
    {
        options.Filters.Add(typeof(ModelStateValidationFilter));
    }).ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressModelStateInvalidFilter = true;
    });
    

通过这种方式,我们不但实现了自定义的模型验证,还通过ActionFilter确保了在所有控制器动作中对模型验证结果的统一处理,从而避免了代码重复,增强了代码的可维护性和一致性。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐