public class ControllerEx : Controller { protected Dictionary<string, string> DicError { get; set; } = new Dictionary<string, string>(); protected ValidatorHub ValidatorHub { get; set; } = new ValidatorHub(); protected override void OnActionExecuted(ActionExecutedContext filterContext) { base.OnActionExecuted(filterContext); ViewData["Error"] = DicError; } protected void ValidatorErrorHandler(ValidationResult result) { foreach (var failure in result.Errors) { if (!this.DicError.ContainsKey(failure.PropertyName)) { this.DicError.Add(failure.PropertyName, failure.ErrorMessage); } } } }
在ControllerEx里我创建了一个ValidatorHub属性,正如其名,他内部存放着各种验证器实体呢。有了它,我们可以在需要验证的Action中通过this.ValidatorHub.具体验证器就能完成具体验证工作了,而不需要再去每次new 一个验证器。
同样我定义了一个DicError的键值对集合,他的键和值类型都是string。key是验证失败的属性名,而value则是验证失败后的错误消息,它是用来存在验证的结果的。
在这里我还定义了一个ValidatorErrorHandler的方法,他有一个参数是验证结果,通过名称我们大致已经猜到功能了,验证错误的处理,对验证结果的错误信息进行遍历,并将错误信息添加至DicError集合。
最终我需要将这个DicError传递给View,简单的办法是ViewData["Error"] 但我不想在每个页面都去这么干,因为这使我要重复多次写这行代码,我会厌倦它的。很棒的是MVC框架为我们提供了Filter(有的地方也称函数钩子,切面编程,过滤器),能够方便我们在生命周期的不同阶段进行控制,很显然,我的需求是在每次执行完Action后要在末尾添加一句ViewData["Error"]=DicError。于是我重写了OnActionExecuted方法,仅添加了 ViewData["Error"] = DicError;
现在我只需要将HomeController继承自ControllerEx即可享受以上所有功能了。
现在基本工作基本都完成了,但我们还忽略了一个问题,我错误是存在了ViewData["Error"]里传递给View,只不过难道我们在验证错误的时候在页面显示一个错误列表?像li一样?这显然不是我们想要的。我们还需要一个帮助我们合理的显示错误信息的函数。在Razor里我们可以对HtmlHelper进行扩展。于是我为HtmlHelper扩展了一个方法ValidatorMessageFor
public static class ValidatorHelper { public static MvcHtmlString ValidatorMessageFor(this HtmlHelper htmlHelper, string property, object error) { var dicError = error as Dictionary<string, string>; if (dicError == null) //没有错误 { // 不会等于空 } else { if (dicError.ContainsKey(property)) { return new MvcHtmlString(string.Format("<p>{0}</p>", dicError[property])); } } return new MvcHtmlString(""); } }
在ValidatorMessaegFor里需要2个参数property 和 error
前者是需要显示的错误属性名,后者则是错误对象即ViewData["Error"],功能很简单,在发现错误对象里存在key为错误属性名的时候将value用一个p标签包裹起来返回,value即为错误属性所对应的错误提示消息。
现在我们还需要在View每一个input下添加一句如: @Html.ValidatorMessageFor("Name", ViewData["Error"])即可。
@using (Html.BeginForm()) { @Html.AntiForgeryToken() <div> <h4>Person</h4> <hr /> @Html.ValidationSummary(true, "", new { @class = "text-danger" }) <div> <label for="Name">姓名</label> <div> <input type="text" /> @Html.ValidatorMessageFor("Name", ViewData["Error"]) </div> </div> <div> <label for="Age">年龄</label> <div> <input type="text" /> @Html.ValidatorMessageFor("Name", ViewData["Error"]) </div> </div> <div> <label for="Home">住址</label> <div> <input type="text" /> @Html.ValidatorMessageFor("Address.Home", ViewData["Error"]) </div> </div> <div> <label for="Phone">电话</label> <div> <input type="text" /> @Html.ValidatorMessageFor("Address.Phone", ViewData["Error"]) </div> </div> <div> <label for="Sex">性别</label> <div> <div> <input type="checkbox" /> </div> </div> </div> <div> <div> <input type="submit" value="Create" /> </div> </div> </div> }
到此我们的所有基本工作都已完成
[HttpPost] public ActionResult ValidatorTest(Person model) { var result = this.ValidatorHub.PersonValidator.Validate(model); if (result.IsValid) { return Redirect("https://www.baidu.com"); } else { this.ValidatorErrorHandler(result); } return View(); }