2014-09-29 67 views
2

这是我创建新用户后的方法:验证应该在CQRS中使用MVC进行验证?

[HttpPost] 
public ActionResult CreateUser(CreateUserViewModel createUserViewModel) 
{ 
    CreateSystemUserCommand createSystemUserCommand = new CreateSystemUserCommand() 
    { 
     Firstname = createUserViewModel.Forename, 
     Surname = createUserViewModel.Surname, 
     Username = createUserViewModel.Username, 
     Password = createUserViewModel.Password 
    }; 

    CreateSystemUserCommandHandler handler = new CreateSystemUserCommandHandler(); 

    handler.Execute(createSystemUserCommand); 

    return RedirectToAction("ViewUsers"); 
} 

上有视图模型一些验证已经,必填字段等,所以UI上会有验证。

但是我想知道如何做到服务器端。

我应该创建一个方法createSystemUserCommand.Validate();

handler.Execute()之前,做handler.Validate()

我该如何将这些错误转换成ModelState?我猜CQRS没有与MVC连接,因此返回特定的模型错误是没有意义的。

有任何想法欢迎。我的直觉是做handler.Validate,因为它将验证逻辑保存在一个类中,并且感觉正确,但我愿意接受建议。

+0

如果您使用HTTP,你将不得不在许多地方,特定的HTTP验证错误。除非你打开公共使用的处理程序,否则我会保持验证结果并将其留在接缝处(例如,在这种情况下,在handler.Execute之前)。我怀疑我会把它放在处理程序中,因为你可能对处理程序中没有意义的http请求有特定的验证逻辑。 – 2014-09-29 16:54:56

回答

2

我不确定您是否使用数据注解,但是对于数据注解,它可以是这样的。 另请参阅其他属性ValidateAntiForgeryToken(可能对您有用)。

[HttpPost] 
[ValidateAntiForgeryToken] 
public ActionResult CreateUser(CreateUserViewModel createUserViewModel) 
{ 
    if (ModelState.IsValid) 
    { 
     CreateSystemUserCommand createSystemUserCommand = new CreateSystemUserCommand() 
     { 
      Firstname = createUserViewModel.Forename, 
      Surname = createUserViewModel.Surname, 
      Username = createUserViewModel.Username, 
      Password = createUserViewModel.Password 
     }; 

     CreateSystemUserCommandHandler handler = new CreateSystemUserCommandHandler(); 

     handler.Execute(createSystemUserCommand); 

     return RedirectToAction("ViewUsers"); 
    } 

    return View(createUserViewModel); 
} 

但是如果你需要复杂的验证你可以去:

if (ModelState.IsValid && handler.Validate()) 

或者你可以实现你自己的验证逻辑,然后通过添加ModelState.AddModelError错误ModelState

4

有2种类型的验证这里,你可能需要:

  • 一个简单的ModelState验证这确保了所需的字段都不缺,int是一个int等。为此,使用数据注释属性就可以做到这一点。

  • 第二种类型是业务逻辑验证 - 这可能需要访问数据库或运行其他验证逻辑以确保数据完整性不受影响。这种验证将在命令级别进行。 要做到这一点,最好的办法是遵循decorator pattern - 包装您的实际处理程序进行验证的处理程序:

    public class ValidationCommandHandlerDecorator<TCommand, TResult> 
        : ICommandHandler<TCommand, TResult> 
        where TCommand : ICommand<TResult> 
    { 
        private readonly ICommandHandler<TCommand, TResult> decorated; 
    
        public ValidationCommandHandlerDecorator(ICommandHandler<TCommand, TResult> decorated) 
        { 
         this.decorated = decorated; 
        } 
    
        [DebuggerStepThrough] 
        public TResult Handle(TCommand command) 
        { 
         var validationContext = new ValidationContext(command, null, null); 
         Validator.ValidateObject(command, validationContext, validateAllProperties: true); 
    
         return this.decorated.Handle(command); 
        } 
    } 
    

    验证的一个例子是:

    public class SomeCustomLogicValidator : IValidator { 
    
        void IValidator.ValidateObject(object instance) { 
         var context = new ValidationContext(instance, null, null); 
    
         // Throws an exception when instance is invalid. 
         Validator.ValidateObject(instance, context, validateAllProperties: true); 
        } 
    }  
    

    然后将其注册为:

    // using SimpleInjector.Extensions; 
    container.RegisterDecorator(
    typeof(ICommandHandler<>), 
    typeof(ValidationCommandHandlerDecorator<>)); 
    

    您可以根据需要包装尽可能多的装饰器,甚至可以将它定义为谓词(确切的语法取决于您使用的DI框架):

    // another decorator 
    container.RegisterDecorator(
    typeof(ICommandHandler<>), 
    typeof(TransactionCommandHandlerDecorator<>)); 
    
    // specific decorator 
    container.RegisterDecorator(
    typeof(ICommandHandler<>), 
    typeof(AccessValidationCommandHandlerDecorator<>), 
    context => !context.ImplementationType.Namespace.EndsWith("Admins")); 
    

我该示例使用DI框架,这使得事情变得更简单,但这种想法可以不使用任何DI容器以及延长。

2

我通常使用FluentValidation在我的应用层(如在命令处理程序)和域层。这些验证器都会抛出异常,我在全局异常处理程序中捕获这些异常,该异常处理程序有责任以正确格式(例如WCF中的错误)将它们传播给使用者。这些消息已经使用正确的语言,基于线程上设置的文化(如果您有多语言站点)。

在网站上,然后用错误的列表。该错误消息是简单地显示并基于所述误差键我可以添加额外的逻辑来禁用控制等

所以在我的情况下的验证是在大多数情况下的服务器侧,仅在应用和领域层定义一次。在客户端可以有一些其他的小输入验证,例如限制用户输入。