2014-09-29 96 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中的錯誤)將它們傳播給使用者。這些消息已經使用正確的語言,基於線程上設置的文化(如果您有多語言站點)。

在網站上,然後用錯誤的列表。該錯誤消息是簡單地顯示並基於所述誤差鍵我可以添加額外的邏輯來禁用控制等

所以在我的情況下的驗證是在大多數情況下的服務器側,僅在應用和領域層定義一次。在客戶端可以有一些其他的小輸入驗證,例如限制用戶輸入。