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



我正在设计一个具有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是否是谷歌地图的有效地址,并且还包含应用程序知道的城市,这意味着这种验证应移动到服务层以保持控制器"薄"。
  2. 服务层现在缺少任何验证逻辑,并假定它始终传递有效数据。这对我来说似乎是错误的,因为服务层似乎应该负责使系统保持一致的有效状态。对此的解决方案是将验证逻辑也添加到服务层,但在我看来,这似乎违反了 DRY 原则。

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

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

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

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

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

相关内容

最新更新