在分层应用程序ASP.NET核心

时间:2018-05-15 16:07:14

标签: c# asp.net-mvc validation asp.net-core

我正在设计一个带有MVC,服务和存储库层的分层Web应用程序,但是我无法知道在何处放置验证逻辑,这使得我可以利用内置表单验证的.NET Core (例如ModelStateDictionary),同时遵循DRY原则。

第一个最明显的方法是使用具有相应数据注释的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));
    }

这很好用,但有几个问题:

  1. 这仅支持基本验证,例如检查字符长度等。在上面的特定示例中,我想根据谷歌地图验证model.Address是有效地址,还包含应用程序知道的城市的,这意味着应该将这种验证移到服务层以保持控制器&#34;瘦&#34;。
  2. 服务层现在缺少任何验证逻辑,并假定它始终传递有效数据。这对我来说似乎不对,因为看起来服务层应该负责将系统保持在一致的有效状态。解决方法是将验证逻辑添加到服务层,但在我看来这似乎违反了DRY原则。
  3. 第二方法是将所有验证逻辑移至服务层,并将所有数据注释移至实际域对象Vendor。这样,每个操作都可以基于数据注释验证模型,并且还应用任何更复杂的逻辑,例如使用谷歌地图验证地址,如前所述。但是,我不确定如何以与MVC Controller相同的方式验证带注释的对象并将字典传回控制器。这个功能似乎特定于MVC,并且会在我的服务层中引入对MVC的依赖,这是不可取的。

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

    我已经看到了几个提出类似问题的问题,但我对任何答案都不满意。其他答案似乎涉及手动编写验证逻辑而不利用数据注释。这是我应该诉诸的吗?

1 个答案:

答案 0 :(得分:0)

您可以创建自己的自定义验证属性,以及开箱即用的属性,例如Required,Range,StringLength等。 我将在下面提供一个例子:

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;
    }
}

您现在可以在地址属性中应用此属性,如下所示:

[Required]
[ValidateAddress(ErrorMessage="Invalid Address")]
public string Address { get; set; }