c# - 如何在 ASP.NET Core 2 MVC 中使用依赖注入设置单元可测试模型验证?
问题描述
我正在构建一个 ASP.NET Core 2 MVC 应用程序。很多时候我需要利用依赖项来验证用户输入。我希望我的验证方法是可单元测试的,并且我希望能够将模拟的依赖项注入其中。这是我之前在 MVC5 中取得的巨大成功,但无法计算出 ASP.NET Core 2 的等效项。
这就是我在 MVC5 中的做法:
// the view model to be validated
public class MyViewModel {
public string Username { get; set; }
}
// the model validator that will have dependencies injected into it
public class MyViewModelValidator : ModelValidator
{
private IUserService users;
private MyViewModel model;
public MyViewModelValidator(ModelMetadata metadata, ControllerContext controllerContext, IUserService users)
: base(metadata, controllerContext)
{
this.users = users;
this.model = base.Metadata.Model as MyViewModel;
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
List<ModelValidationResult> errors = new List<ModelValidationResult>();
if (this.users.CheckExists(this.model.Username))
{
errors.Add(new ModelValidationResult() { MemberName = nameof(MyViewModel.Username), Message = "Username is not available" });
}
return errors;
}
}
// this class works out which validator is required for a given model and
// injects the appropriate dependencies that is resolves using unity in my
// in my case
public class ViewModelValidatorProvider : ModelValidatorProvider
{
private IUnityContainer container;
public ViewModelValidatorProvider() => this.container = DependencyResolver.Current.GetService<IUnityContainer>();
public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)
{
if (metadata.ModelType == typeof(MyViewModel))
yield return new MyViewModelValidator(metadata, context, this.container.Resolve<IUserService>());
}
}
// the provider is hooked up in the app start in Global.asax.cs file
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
ModelValidatorProviders.Providers.Add(new ViewModelValidatorProvider());
}
}
现在我可以创建一个带有模拟依赖项的验证器实例,然后我就走了!遗憾的是 ASP.NET Core 2 没有ModelValidator
该类,到目前为止我发现的所有内容似乎都想通过控制器注入依赖项或在IValidatableObject
sValidate()
函数中解决它们。
是否可以在 MVC Core 中执行此操作?
解决方案
因此,在@Nkosi 对这个问题发表评论后,我开始走上正确的道路(我认为),最终实现了一个基于类型过滤器的验证系统。
首先,我有一个基本的验证器模型,我们需要在我们的类型过滤器中实现它:
public abstract class BaseViewModelValidator<TModel> : IAsyncActionFilter
where TModel : class
{
public async virtual Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// get the model to validate
if (context.ActionArguments["model"] is TModel model)
await this.ValidateAsync(model, context.ModelState);
else
throw new Exception($"View model of type `{context.ActionArguments["model"].GetType()}` found, type of `{typeof(TModel)}` is required.");
await next();
}
public abstract Task ValidateAsync(TModel model, ModelStateDictionary state);
}
然后,因为将它用作命名属性而不是 更好,所以[TypeFilter(typeof(SomeActionFilter))]
我创建了一个TypeFilterAttribute
包装我的基本验证器的实现,如下所示:
public class DemoViewModelValidatorAttribute : TypeFilterAttribute
{
public DemoViewModelValidatorAttribute()
: base(typeof(DemoViewModelValidator))
{
}
internal class DemoViewModelValidator : BaseViewModelValidator<DemoViewModel>
{
private readonly ISomeService service;
// dependencies are injected here (assuming you've registered them in the start up)
public DemoViewModelValidator(ISomeService service) => this.service = service;
public async override Task ValidateAsync(DemoViewModel model, ModelStateDictionary state)
{
if (await this.service.CheckSomethingAsync(model))
state.AddModelError(nameof(model.SomeProperty), $"Whoops!!!");
}
}
}
DemoViewModelValidator
然后,您可以对您的内容进行单元测试!希望有人觉得这很有用!
推荐阅读
- azure-devops - 有没有办法(永久)链接 Azure DevOps wiki 页面中的源代码片段?
- data-structures - 一元运算符后缀问题的中缀
- python - 我如何看到包“python-magic”应该作为“magic”导入?
- typescript - 打字稿:在课堂上无法识别
- aem - 如何在 AEM 中的自定义组件上使用子编辑器?
- ios - 尝试从我的 react native 应用程序打开 viber 应用程序。(iOS)
- python-3.7 - python类计算两个值的和和差
- python - Numpy - 创建序数类别嵌入
- php - Laravel - 将集合转换为简单数组
- powershell - 什么是 MSBUILD : Unknown switch 错误解决方案?