C# 分层应用程序ASP.NET核心中的验证

C# 分层应用程序ASP.NET核心中的验证,c#,asp.net-mvc,validation,asp.net-core,C#,Asp.net Mvc,Validation,Asp.net Core,我正在设计一个带有MVC、服务和存储库层的分层web应用程序,但是我很难知道在遵循DRY原则的同时将验证逻辑放在何处,从而可以利用.NET核心内置表单验证(例如ModelStateDictionary) 第一个也是最明显的方法是使用具有适当数据注释的ViewModel: public class VendorViewModel { public long Id { get; set; } [Required] public string Name { get; set

我正在设计一个带有MVC、服务和存储库层的分层web应用程序,但是我很难知道在遵循DRY原则的同时将验证逻辑放在何处,从而可以利用.NET核心内置表单验证(例如
ModelStateDictionary

第一个也是最明显的方法是使用具有适当数据注释的
ViewModel

public class VendorViewModel
{

    public long Id { get; set; }

    [Required]
    public string Name { get; set; }


    [Required]
    public string Phone { get; set; }


    [Required]
    public string Email { get; set; }


    [Required]
    public string Address { get; set; }


    public DateTime? VerifiedAt { get; set; }

}
然后,我的控制器操作将如下所示

   public async Task<IActionResult> Create([FromForm] VendorViewModel model)
    {
        await AuthorizePolicyAsync(AuthorizationPolicyTypes.Vendor.Create);
        if (!ModelState.IsValid) //Validation problems, so re-display the form.
            return View(model);

        await _vendorservice.CreateVendorAsync(model.Name,model.Phone,model.Email,model.Address,null);
        return RedirectToAction(nameof(Index));
    }
公共异步任务创建([FromForm]VendorViewModel)
{
等待AuthorizationPolicyAsync(AuthorizationPolicyTypes.Vendor.Create);
如果(!ModelState.IsValid)//验证问题,则重新显示表单。
返回视图(模型);
wait_vendorservice.createvendorsync(model.Name,model.Phone,model.Email,model.Address,null);
返回重定向到操作(名称(索引));
}
这很好,但是有两个问题:

  • 这只支持基本验证,如检查字符长度等。在上面的特定示例中,我想验证
    model.Address
    是否根据谷歌地图是有效地址,并且还包含应用程序知道的城市,这意味着这种验证应该转移到服务层,以保持控制器“精简”
  • 服务层现在缺少任何验证逻辑,并假定始终向其传递有效数据。在我看来,这似乎是错误的,因为服务层应该负责保持系统处于一致的有效状态。解决这个问题的一个方法是向服务层添加验证逻辑,但在我看来,这似乎违反了DRY原则
  • 第二种方法是将所有验证逻辑移动到服务层,并将所有数据注释移动到实际的域对象
    供应商
    。这样,每个操作都可以基于数据注释验证模型,还可以应用任何更复杂的逻辑,如前面提到的使用谷歌地图验证地址。但是,我不确定如何像MVC
    控制器那样验证带注释的对象,并将字典传回控制器。此功能似乎是MVC特有的,并且会在我的服务层中引入对MVC的依赖,这是不可取的

    我是否可以在运行时将验证逻辑优雅地移动到服务层 利用数据注释和MVC内置的
    ModelStateDictionary
    ?如何将错误列表返回控制器?如果发生任何验证错误,我是否会抛出异常并在控制器中捕获它


    我见过几个问题问了一个类似的问题,但我对任何答案都不满意。其他答案似乎涉及手动编写验证逻辑,而不是利用数据注释。这就是我应该采用的方法吗?

    除了可直接使用的属性(如所需、范围、字符串长度等)之外,您还可以创建自己的自定义验证属性。 我将在下面提供一个例子:

    public class ValidateAddressAttribute : Attribute, IModelValidator
    {
        public bool IsRequired => true;
        public string ErrorMessage { get; set; } = "Address is not valid";
    
        public IEnumerable<ModelValidationResult>Validate(ModelValidationContext context) 
        {
           List<ModelValidationResult> validationResults = new List<ModelValidationResult>();
           string address = context.Model as string;
           if(!IsAddressValid(address))
           {
             validationResults.Add(new ModelValidationResult("", ErrorMessage));            
           }
           return validationResults;
    
        }
    
        private bool IsAddressValid(string address)
        {
           bool isAddressValid;
           //set isAddressValid to true or false based on your validation logic
           return isAddressValid;
        }
    }
    

    我该把这个放在哪里?看起来我需要把它放在服务层,这样我也可以让服务层执行验证,但是它向MVC添加了一个依赖项,因为IModelValidator是在MVC中定义的。它奇妙地解释了如何在不依赖aspnet mvc的情况下创建用于验证的服务层,以便可以将此服务层与其他前端(如wpf)一起重用(通过使用ModelStateWrapper实现)。此外,还可以使用[Required]等属性在System.ComponentModel.DataAnnotations中定义,而不是MVC框架的一部分。所以,如果需要,您可以使用类来验证在服务层中使用数据注释的属性。嗨,Brad,您得到正确的响应了吗?
    [Required]
    [ValidateAddress(ErrorMessage="Invalid Address")]
    public string Address { get; set; }