Итак, после публикации @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
для своего сердца!Надеюсь, кто-то найдет это полезным!