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失效的话,那岂不是根本显示不出来?

综上所述,于是我决定就让它这样简单而愉快的结束吧。到此我们的前后端校验功能就算全部实现。

由于博文是前五篇的延续,在一些重复的内容上,不过多介绍,如果您在阅读时,有任何疑问,请从前面开始阅读。

posted @ 2018-07-21 10:00  Gxqsd  阅读(674)  评论(3编辑  收藏  举报