【问题标题】:Validation in a layered application ASP.NET Core分层应用程序 ASP.NET Core 中的验证
【发布时间】:2018-10-25 13:18:51
【问题描述】:

我正在设计一个带有 MVC、Service 和 Repository 层的分层 Web 应用程序,但是我不知道在哪里放置允许我利用 .NET Core 内置表单验证的验证逻辑(例如 @987654321 @),同时遵循 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 是根据谷歌地图的有效地址,并且还包含应用程序知道的城市,这意味着应该将这种验证移至服务层以保持控制器“瘦”。
  2. 服务层现在缺少任何验证逻辑,并假定它始终被传递有效数据。这对我来说似乎是错误的,因为服务层似乎应该负责使系统保持一致的有效状态。解决这个问题的方法是向服务层添加验证逻辑,但我认为这似乎违反了 DRY 原则。

第二种方法是将所有验证逻辑移动到服务层,并将我的所有数据注释移动到实际的域对象Vendor。这样,每个操作都可以根据数据注释验证模型,还可以应用任何更复杂的逻辑,例如使用谷歌地图验证地址,如前所述。但是,我不确定如何以与 MVC Controller 相同的方式验证带注释的对象并将字典传回控制器。此功能似乎特定于 MVC,并且会在我的服务层中引入对 MVC 的依赖,这是不可取的。

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

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

【问题讨论】:

  • 嗨布拉德,你得到了正确的回应吗?

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


【解决方案1】:

您可以创建自己的自定义验证属性,以及开箱即用的属性,例如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; }

【讨论】:

  • 我会把这个放在哪里?似乎我需要将它放在服务层中,这样我也可以让服务层执行验证,但是由于 IModelValidator 是在 MVC 中定义的,因此它向 MVC 添加了依赖项。
  • 请您阅读Validating with service layer 文章。它很好地解释了如何在不依赖 aspnet mvc 的情况下创建一个用于验证的服务层,以便您可以将此服务层与其他前端(例如 wpf)重用(通过使用 ModelStateWrapper 实现)。
  • 此外,像 [Required] 这样的属性是在 System.ComponentModel.DataAnnotations 中定义的,而不是 MVC 框架的一部分。因此,如果需要,您可以使用Validator 类来验证在服务层中使用数据注释的属性。
猜你喜欢
  • 2012-05-11
  • 1970-01-01
  • 2019-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-26
  • 2022-01-03
  • 1970-01-01
相关资源
最近更新 更多