【问题标题】:How do you setup unit testable model validation with dependency injection in ASP.NET Core 2 MVC?如何在 ASP.NET Core 2 MVC 中使用依赖注入设置单元可测试模型验证?
【发布时间】:2018-11-05 00:08:42
【问题描述】:

我正在构建一个 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 类,到目前为止我发现的所有东西似乎都想通过控制器注入依赖项或在 IValidatableObjects Validate() 函数中解决它们。

在 MVC Core 中可以做到这一点吗?

【问题讨论】:

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


【解决方案1】:

因此,在@Nkosi 对这个问题发表评论后,我开始走上正确的道路(我认为),最终实现了一个基于type filters 的验证系统。

首先,我有一个基本的验证器模型,我们需要在我们的类型过滤器中实现它:

    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 进行单元测试,让您心满意足!希望有人觉得这很有用!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-21
    • 1970-01-01
    • 1970-01-01
    • 2018-10-31
    • 1970-01-01
    相关资源
    最近更新 更多