Ric*_*ard 12 c# dependency-injection fluentvalidation asp.net-web-api
在MVC中,我可以创建一个可以采用依赖关系的模型验证器.我通常使用FluentValidation.例如,这允许我检查帐户注册是否未使用电子邮件地址(注意:这是一个简化的示例!):
public class RegisterModelValidator : AbstractValidator<RegisterModel> {
private readonly MyContext _context;
public RegisterModelValidator(MyContext context) {
_context = context;
}
public override ValidationResult Validate(ValidationContext<RegisterModel> context) {
var result = base.Validate(context);
if (context.Accounts.Any(acc => acc.Email == context.InstanceToValidate.Email)){
result.Errors.Add(new ValidationFailure("Email", "Email has been used"));
}
return result;
}
}
Run Code Online (Sandbox Code Playgroud)
使用FluentValidation的Web API不存在此类集成.已经有夫妇的尝试,在这一点,但也没有解决的依赖注入方面,只有静态验证工作.
这很困难的原因是由于MVC和Web API之间的ModelValidatorProvider和ModelValidator的实现不同.在MVC中,这些是按请求实例化的(因此注入上下文很容易).在Web API中,它们是静态的,ModelValidatorProvider为每种类型维护ModelValidators的缓存,以避免对每个请求进行不必要的反射查找.
我一直试图自己添加必要的集成,但一直试图获得依赖范围.相反,我想我会退后一步,询问是否还有其他问题的解决方案 - 如果有人提出了执行模型验证的解决方案,可以注入依赖关系.
我不想在Controller中执行验证(我使用ValidationActionFilter来保持这一点),这意味着我无法从控制器的构造函数注入中获得任何帮助.
我能够使用GetDependencyScope()扩展方法注册然后从请求访问Web API依赖项解析器.这允许在执行验证过滤器时访问模型验证器.
如果这不能解决您的依赖注入问题,请随时澄清.
Web API配置(使用Unity作为IoC容器):
public static void Register(HttpConfiguration config)
{
config.DependencyResolver = new UnityDependencyResolver(
new UnityContainer()
.RegisterInstance<MyContext>(new MyContext())
.RegisterType<AccountValidator>()
.RegisterType<Controllers.AccountsController>()
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Run Code Online (Sandbox Code Playgroud)
验证操作过滤器:
public class ModelValidationFilterAttribute : ActionFilterAttribute
{
public ModelValidationFilterAttribute() : base()
{
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
var scope = actionContext.Request.GetDependencyScope();
if (scope != null)
{
var validator = scope.GetService(typeof(AccountValidator)) as AccountValidator;
// validate request using validator here...
}
base.OnActionExecuting(actionContext);
}
}
Run Code Online (Sandbox Code Playgroud)
模型验证器:
public class AccountValidator : AbstractValidator<Account>
{
private readonly MyContext _context;
public AccountValidator(MyContext context) : base()
{
_context = context;
}
public override ValidationResult Validate(ValidationContext<Account> context)
{
var result = base.Validate(context);
var resource = context.InstanceToValidate;
if (_context.Accounts.Any(account => String.Equals(account.EmailAddress, resource.EmailAddress)))
{
result.Errors.Add(
new ValidationFailure("EmailAddress", String.Format("An account with an email address of '{0}' already exists.", resource.EmailAddress))
);
}
return result;
}
}
Run Code Online (Sandbox Code Playgroud)
API控制器操作方法:
[HttpPost(), ModelValidationFilter()]
public HttpResponseMessage Post(Account account)
{
var scope = this.Request.GetDependencyScope();
if(scope != null)
{
var accountContext = scope.GetService(typeof(MyContext)) as MyContext;
accountContext.Accounts.Add(account);
}
return this.Request.CreateResponse(HttpStatusCode.Created);
}
Run Code Online (Sandbox Code Playgroud)
型号(示例):
public class Account
{
public Account()
{
}
public string FirstName
{
get;
set;
}
public string LastName
{
get;
set;
}
public string EmailAddress
{
get;
set;
}
}
public class MyContext
{
public MyContext()
{
}
public List<Account> Accounts
{
get
{
return _accounts;
}
}
private readonly List<Account> _accounts = new List<Account>();
}
Run Code Online (Sandbox Code Playgroud)
我终于成功了,但这有点困难。如前所述,ModelValidatorProvider 将保留所有验证器的 Singleton 实例,因此这是完全不合适的。相反,我按照反对派的建议使用过滤器来运行我自己的验证。该过滤器可以访问IDependencyScope
并可以巧妙地实例化验证器。
在过滤器中,我遍历ActionArguments
,并使它们通过验证。验证代码是从 的 Web API 运行时源中复制出来的DefaultBodyModelValidator
,并进行修改以在DependencyScope
.
最后,要使其与 一起工作ValidationActionFilter
,您需要确保过滤器按特定顺序执行。
我已经在github上打包了我的解决方案,并在 nuget 上提供了一个版本。
归档时间: |
|
查看次数: |
7989 次 |
最近记录: |