我试图在我的应用程序中实现验证策略.我有一个MVC层,服务层,存储库和域POCO.现在在MVC层,我使用我的视图模型上的数据注释来验证用户输入,允许我给用户快速反馈.在控制器中,我调用ModelState.IsValid来检查输入,然后再使用automapper设置域对象.
这里是我的烦恼.我将域对象传递到需要根据我的业务规则进行验证的服务,但是如何将验证错误传递给控制器?我发现的例子做了以下一个:
>在服务层中抛出异常并捕获Contoller.但这似乎是错误的,当然例外是例外情况,我们应该返回一些有意义的东西.
>使用ModelStateWrapper并将ModelStateDictionary注入到服务中.但是这种方法最终展开为循环依赖(控制器取决于服务,服务依赖于控制器),这似乎是一个坏的代码气味.
>将验证方法添加到POCO.这样做的问题是,业务规则可能依赖于其他POCO对象,所以肯定这应该在可以访问所需表和对象的服务中完成.
有没有一个更简单的方法我失踪了?我已经看到很多关于这个问题,但没有具体的解决办法,而不是上面提到的.我认为在验证中的任何方法可以传回一些我可以在控制器中使用的键/值对象,但不确定此策略是否可能在以后出现问题.
解决方法
我认为相当优雅的方式是在您的服务上使用一个有效的方法,从业务逻辑返回模型错误字典.这样,没有将ModelState注入到服务中 – 该服务只是进行验证并返回任何错误.然后由控制器将这些ModelState错误合并到其ViewData中.
因此,服务验证方法可能如下所示:
public IDictionary<string,string> ValidatePerson(Person person) { Dictionary<string,string> errors = new Dictionary<string,string>(); // Do some validation,e.g. check if the person already exists etc etc // Add model erros e.g.: errors.Add("Email","This person already exists"); }
然后您可以在控制器中使用扩展方法将这些错误映射到ModelState上,如下所示:
public static class ModelStateDictionaryExtensions { public static void Merge(this ModelStateDictionary modelState,IDictionary<string,string> dictionary,string prefix) { foreach (var item in dictionary) { modelState.AddModelError((string.IsNullOrEmpty(prefix) ? "" : (prefix + ".")) + item.Key,item.Value); } } }
然后您的控制器将使用:
ModelState.Merge(personService.ValidatePerson(person),"");