ASP.NET全栈开发验证模块之将前后台校验结合
前五篇博文分别介绍并实现了前端校验和服务器校验,这篇博文主要是介绍如何将两者结合起来使用,并总结。
之前,我们在ASP.NET MVC中集成了基于FluentValidator的验证器,并通过扩展Controller,在ControllEx中 使用 OnActionExecuting 进行统一校验。最后将所有错误信息存放在ViewData["Error"]内部后返回视图。
在视图呈现方面,我们使用了HtmlHelper的扩展方法来帮呈现,这样有效避免了Null的尴尬局面。
/// <summary> /// 显示指定属性的错误消息 /// </summary> /// <param name="htmlHelper">HtmlHelper</param> /// <param name="property">指定的属性</param> /// <param name="error">ViewData["Error"]</param> /// <param name="tagType">标签类型</param> /// <param name="htmlAttribute">标签属性</param> /// <returns></returns> public static MvcHtmlString ValidatorMessageFor(this HtmlHelper htmlHelper, string property, object error, string tagType = "p", object htmlAttribute = null) { var dicError = error as Dictionary<string, string>; if (dicError != null) //没有错误 { if (dicError.ContainsKey(property)) { StringBuilder sb = new StringBuilder(); if (htmlAttribute != null) { htmlAttribute.GetType().GetProperties().ToList().ForEach((p) => { string key = p.Name; string value = p.GetValue(htmlAttribute).ToString(); sb.AppendFormat(" {0}='{1}' ", key, value); }); } return new MvcHtmlString(string.Format("<{0} {2}>{1}</{0}>", tagType, dicError[property], sb.ToString())); } } return new MvcHtmlString(""); }
如今我对HtmlHelper做了一定修改,使它支持自定义标签类型和定义Html的属性,这会让我们好的管理呈现效果。比方为他加一个样式。
通过以上步骤我们就实现了服务端的验证。
在前端,与前几章说讲的一样,我们使用Vue,并自编写了基于Vue的验证插件vuefluentvalidator,为什么叫它vuefluentvalidator,因为编写它的时候就是借鉴的咱们后台验证框架FluentValidator的使用方式。
@using ValidationWebTest.Mvc.MvcHelperEx @{ ViewBag.Title = "ValidatorTest"; } <h2>ValidatorTest</h2> <div id="box"> @using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-horizontal"> <h4>Person</h4> <hr /> @Html.ValidationSummary(true, "", new { @class = "text-danger" }) <div class="form-group"> <label for="Name" class="control-label col-md-2">姓名</label> <div class="col-md-10"> <input type="text" name="Name" class="form-control" v-model="model.name" /> <p v-text="model.error.name"></p> @Html.ValidatorMessageFor("Name", ViewData["Error"], "span", new { @class = "text-info" }) </div> </div> <div class="form-group"> <label for="Age" class="control-label col-md-2">年龄</label> <div class="col-md-10"> <input type="text" name="Age" class="form-control" v-model="model.age" /> <p v-text="model.error.age"></p> @Html.ValidatorMessageFor("Age", ViewData["Error"], "strong") </div> </div> <div class="form-group"> <label for="Home" class="control-label col-md-2">住址</label> <div class="col-md-10"> <input type="text" name="Address.Home" class="form-control" v-model="model.address.home" /> <p v-text="model.error.address.home"></p> @Html.ValidatorMessageFor("Address.Home", ViewData["Error"]) </div> </div> <div class="form-group"> <label for="Phone" class="control-label col-md-2">电话</label> <div class="col-md-10"> <input type="text" name="Address.Phone" class="form-control" v-model="model.address.phone" /> <p v-text="model.error.address.phone"></p> @Html.ValidatorMessageFor("Address.Phone", ViewData["Error"]) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="Create" class="btn btn-default" v-on:click="formSubmit($event)" /> </div> </div> </div> } </div> @section scripts{ <script src="~/Content/vue.js"></script> <script src="~/Content/vuefluentvalidator.js"></script> <script> let vm = new Vue({ el: '#box', data: { validator: new Validator({ model: { name: undefined, age: undefined, address: { home: undefined, phone: undefined }, }, rule: function (than) { than.ruleFor("name") .NotEmpty() .WithMessage("姓名不能为空") .MinimumLength(5) .WithMessage("最短长度为5"); than.ruleFor("age") .NotEmpty() .WithMessage("年龄不能为空"); than.ruleFor("address.home") .NotEmpty() .WithMessage("家庭地址不能为空"); than.ruleFor("address.phone") .NotEmpty() .WithMessage("电话不能为空"); } }), }, methods: { formSubmit: function (ev) { if (this.validator.passValidation()) { return; } ev.preventDefault(); this.validator.validation(ev.target); } }, computed: { model: function () { return this.validator.model; } } }); </script> }
使用方式和之前前端验证所讲的都一模一样,只是在验证下边加了一行
@Html.ValidatorMessageFor("Name", ViewData["Error"], "span", new { @class = "text-info" })
为什么我会这样写?因为在正常情况下,我们前端会进行校验,如果前端校验不通过的时候,请求不会发送到后台,所以这一行在正常情况下是用不到的。
那在什么时候会用到他?在前端校验失效时或伪造请求时。
首先分析第一种
前端校验失效,也就意味着javascript失效,这种情况下我们的
<p v-text="model.error.name"></p>
什么都显示不出来。
而在验证失败的时候 @Html.ValidatorMessageFor("Name", ViewData["Error"], "span", new { @class = "text-info" }) 就会起作用了,由于我们对标签的呈现效果控制的灵活性,在视觉体验上,没有任何区别。
如果在第二种情况下,他都不是一个正常的请求,我管它干什么?阻止就是了。
如果你认为既然使用Vue,那为什么不将错误信息绑定到我们的验证器上呢?
事实上我也想过这么做,一是它的工作量比现在这种方式更大(原谅我偷了个懒),二是,如果javascript失效的话,那岂不是根本显示不出来?
综上所述,于是我决定就让它这样简单而愉快的结束吧。到此我们的前后端校验功能就算全部实现。
由于博文是前五篇的延续,在一些重复的内容上,不过多介绍,如果您在阅读时,有任何疑问,请从前面开始阅读。