zoukankan      html  css  js  c++  java
  • MVC验证10-到底用哪种方式实现客户端服务端双重异步验证

    本篇将通过一个案例来体验使用MVC的Ajax.BeginForm或jQuery来实现异步提交,并在客户端和服务端双双获得验证。希望能梳理、归纳出一个MVC异步验证的通用解决思路。本篇主要涉及:

    1、通过Ajax.BeginForm()方式,返回部分视图显示验证信息。
    2、通过jQuery+Html.BeginForm()方式,返回部分视图显示验证信息。
    3、通过jquery,返回json字符串,json字符串中包含部分视图及验证信息。

    此外,如下2篇是本文的"兄弟篇",只不过没有像本篇这样把多种实现方式放在一个案例中实现。

    MVC验证08-jQuery异步验证:通过jquery,返回字符串,并把错误信息精确显示到指定html元素。
    MVC验证09-使用MVC的Ajax.BeginForm方法实现异步验证:通过Ajax.BeginForm方式,返回部分视图显式验证信息。

      准备工作

    □ 实现客户端验证所需的js文件

    不管js文件是放在_Layout.cshtml中,还是放在具体的视图页,也不管BundleConfig.cs中捆版了那些js和css。以下js文件是必须的:
    1、jquery的某个版本
    2、jquery.validate.js
    3、jquery.validate.unobtrusive.js

    □ 实现客户端验证的配置

    在网站Web.config中,相关的属性必须设置为true:

      <appSettings>
          ...
        <add key="ClientValidationEnabled" value="true" />
        <add key="UnobtrusiveJavaScriptEnabled" value="true" />
      </appSettings>

    □ 即将用到的View Model

    using System.ComponentModel.DataAnnotations;
    using jan.Extension;
     
    namespace jan.Models
    {
        public class Customer
        {
            [Required]
            [ValidUserNameAttribue(ErrorMessage = "用户名只能为darren")]
            [Display(Name = "用户名")]
            public string UserName { get; set; }
        }
    }

    □ 自定义验证特性ValidUserNameAttribue

    using System.ComponentModel.DataAnnotations;
     
    namespace jan.Extension
    {
        public class ValidUserNameAttribue : ValidationAttribute
        {
            public override bool IsValid(object value)
            {
                //只有同时满足2个条件就让通过,否则验证失败
                return (value != null && value.ToString() == "darren");
            }
        }
    }

      1、通过Ajax.BeginForm方式,返回部分视图显示验证信息

    □ 1、1 Index.cshtml视图

    如果把Index.cshtml看作主视图的话,需要异步获取的内容放在部分视图中,主视图通过Html.Partial()来显示部分视图内容。

    @model jan.Models.Customer
     
    @{
        ViewBag.Title = "Index";
        Layout = "~/Views/Shared/_Layout.cshtml";
    }
     
    @DateTime.Now:  Index.cshtml视图被渲染
    <hr/>
     
    <div id="FormContainer">
            @Html.Partial("_Form")
    </div>

    □ 1.2 部分视图_Form.cshtml,验证失败返回的部分视图

    用Ajax.BeginForm()方法实现。

    @model jan.Models.Customer
     
    @DateTime.Now: _Form.cshtml视图被渲染
    <hr/>
     
    @using (Ajax.BeginForm("ValidCustomer", new AjaxOptions() { UpdateTargetId = "FormContainer", OnSuccess = "$.validator.unobtrusive.parse('form');" }))
    {
        <p>
            @Html.LabelFor(m => m.UserName):
            @Html.EditorFor(m => m.UserName)
        </p>
        <p style="color:red;">
            @Html.ValidationMessageFor(m => m.UserName)
        </p>
        <input type="submit" value="提交"/>
    }
     

    UpdateTargetId = "FormContainer"中的FormContainer是主视图的div,部分视图异步提交返回的内容显示到id为FormContainer的div中。
    OnSuccess = "$.validator.unobtrusive.parse('form');" 每次提交完后再初始化表单,准备下一次被提交。

    □ 1.3 _Success.cshtml,验证成功返回的部分视图

    @model jan.Models.Customer
    @Model.UserName 是有效的

    □ 1.4 HomeController

    using System.Web.Mvc;
    using jan.Models;
     
    namespace jan.Controllers
    {
        public class HomeController : Controller
        {
            public ActionResult Index()
            {
                return View(new Customer());
            }
     
            [HttpPost]
            public ActionResult ValidCustomer(Customer customer)
            {
                return PartialView(!ModelState.IsValid ? "_Form" : "_Success", customer);
            }
        }
    }
     

    □ 1.5 效果

    提交之前:

    1.1


    没有填写任何内容,提交报错:

    1.2


    没有输入darren,提交报错:

    1.3


    输入正确,提交成功:

    1.4

      2、通过jQuery+Html.BeginForm方式,返回部分视图显示验证信息

    □ 2.1 Index.cshtml视图

    增加了一个动态显示加载的div,使用了jquery ui的progress dialog,提交的时候显示加载图片。

    展开


    □ 2.2 部分视图_Form.cshtml,验证失败返回的部分视图

    点击"提交"触发jquery中的表单提交事件。

    @model jan.Models.Customer
     
    @DateTime.Now: _Form.cshtml视图被渲染
    <hr/>
     
    @using (Html.BeginForm("ValidCustomer", "Home"))
    {
        
        <p style="color:red;">
             @Html.ValidationMessageFor(m => m.UserName)
        </p>
        
        <p>
            @Html.LabelFor(m => m.UserName):
            @Html.EditorFor(m => m.UserName)
        </p>
     
        <input type="submit" value="提交" />
    }
     

    □ 2.3 _Success.cshtml,验证成功返回的部分视图

    @model jan.Models.Customer
    @Model.UserName 是有效的

    □ 2.4 HomeController

    其中的Thread.Sleep(2000)是模拟请求时间稍长,前台视图显式加载效果。

    using System.Web.Mvc;
    using jan.Models;
     
    namespace jan.Controllers
    {
        public class HomeController : Controller
        {
            public ActionResult Index()
            {
                return View(new Customer());
            }
     
            [HttpPost]
            public ActionResult ValidCustomer(Customer customer)
            {
                Thread.Sleep(2000); 
                return PartialView(!ModelState.IsValid ? "_Form" : "_Success", customer);
            }
        }
    }
     

    □ 2.5 效果

    提交之前:

    2.1


    没有填写任何内容,提交报错:

    2.2


    没有输入darren,提交报错:

    2.2没有冒泡之前


    虽然报错,但注意到存在一个问题:地址变成了/Home/ValidCustomer?而在Index.cshtml的jquery中,让每次提交成功后,返回的部分视图渲染到Index.cshtml的id为FormContainer的div中。为什么?

    每次返回部分视图被渲染到Index.cshtm中id为FormContainer的div中,这部分属动态内容,而类似$("form").on("submit", function (event)这样的写法,对动态内容是无效的。根据"DOM冒泡"的事实,应该把submit事件注册给form的父元素,当点击form中的提交按钮,根据"DOM冒泡",触发了form父元素的submit事件,而包含在form父元素下的所有动态内容,此时会受到submit事件的影响。Index.cshtm中完整js如下:

    展开

    再次输入错误的用户名,提交,也报错,但没有跳转到/Home/ValidCustomer。

    2.2没有冒泡之后


    输入正确,提交成功:

    1.4

      3、通过jquery,返回json字符串,json字符串中包含部分视图及验证信息

    □ 3.1 HomeController

    返回给前台json字符串,一个key用来提示是否验证成功,一个key是部分视图的html元素字符串。

    using System.Web.Mvc;
    using jan.Extension;
    using jan.Models;
    using System.Threading;
     
    namespace jan.Controllers
    {
        public class HomeController : Controller
        {
            public ActionResult Index()
            {
                return View(new Customer());
            }
     
            [HttpPost]
            public ActionResult ValidCustomer(Customer customer)
            {
                Thread.Sleep(2000);
                if (!ModelState.IsValid)
                {
                    return Json(new { vd = false, pv = this.RenderPartialViewToString("_Form", customer) });
                }
                return Json(new { vd = true, pv = this.RenderPartialViewToString("_Success", customer) });
            }
        }
    }
     

    □ 3.2 需要一个扩展方法,能把部分视图、model、以及错误信息转换成字符串

    展开

    □ 3.3 Index.cshtml视图

    这一次,不再需要部分视图,所有的提交和返回数据都发生在一个视图页面上。

    注意: 
    不要直接给提交按钮注册click事件,$('#btn').on("click", function(event),这样会对动态生成的内容无效。而应该这样写:$('#FormContainer').on("click","#btn", function(event)

    展开

    □ 3.4 效果

    没有输入任何信息,提交,报错:

    3.1

    输入错误用户名,提交:

    3.2


    发现,当第二次输入错误信息,提交,竟然跳转到了Home/ValidCustomer,而且返回的是json字符串。为什么?

    当第一输入错误信息,提交到控制器方法,当验证失败,会执行return Json(new { vd = false, pv = this.RenderPartialViewToString("_Form", customer) });返回的是_Form.cshtml部分视图,虽然第一次验证失败,没有跳转,但实际上,Index.cshtml的<div id="FormContainer">中已经有了_Form.cshtml部分视图视图内容:

    展开



    审查第一次提交后的html元素,如图:

    3.3

    解决方法:
    让控制器返回部分视图字符串的时候,不要再返回_Form.cshtml部分视图字符串。
    可以自定义针对Customer的一个视图,该视图中不仅有input,提交按钮,而且还有错误信息。

    关于自定义MVC视图引擎、视图,请参考:
    自定义MVC视图引擎ViewEngine 创建Model的专属视图     

    ■ 3.4.1 实现IView接口

    using jan.Models;
    using System.Web.Mvc;
    using System.Web.Mvc.Html;
     
    namespace jan.Extension
    {
        public class CustomerView : IView
        {
            
            public void Render(ViewContext viewContext, System.IO.TextWriter writer)
            {
                var allErrors = viewContext.ViewData.ModelState["UserName"].Errors;
                string msg = string.Empty;
                foreach (ModelError error in allErrors)
                {
                    msg = msg + error.ErrorMessage + " ";
                }
                //自定义输出视图的html格式
                writer.Write("<p style='color:red'>"+msg+"</p>");
                writer.Write("<p>用户名:<input type='text' id='UserName' /></p>");
                writer.Write("<p><input type='button' id='btn' value='提交' /></p>");
            }
        }
    }
     

    可以在ViewContext中根据某个属性拿到错误信息:viewContext.ViewData.ModelState["UserName"].Errors。

    ■ 3.4.2 实现IViewEngine接口

    让ViewEngine工作的时候,如果发现部分视图名是CustomerView,就返回自定义视图。

    using System;
    using System.Web.Mvc;
     
    namespace jan.Extension
    {
        public class CustomerViewEngine : IViewEngine
        {
            public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
            {
                if (partialViewName == "CustomerView")
                {
                    return new ViewEngineResult(new CustomerView(), this);
                }
                else
                {
                    return new ViewEngineResult(new String[]{"针对Customer的视图还没创建!"});
                }
            }
     
            public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
            {
                if (viewName == "CustomerView")
                {
                    return new ViewEngineResult(new CustomerView(), this);
                }
                else
                {
                    return new ViewEngineResult(new String[] { "针对Customer的视图还没创建!" });
                }
            }
     
            public void ReleaseView(ControllerContext controllerContext, IView view)
            {
            }
        }
    }
     

    ■ 3.4.3 Controller的静态扩展方法

    关键代码:ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
    这时候,ViewEngines一旦找到部分视图名称是CustomerView,就会返回自定义视图的ViewEngineResult,最终写进流,返回自定义视图字符串。

    展开

    ■ 3.4.4 =HomeController中,验证失败,返回自定义部分视图

    using System.Web.Mvc;
    using jan.Extension;
    using jan.Models;
    using System.Threading;
     
    namespace jan.Controllers
    {
        public class HomeController : Controller
        {
            public ActionResult Index()
            {
                return View(new Customer());
            }
     
            [HttpPost]
            public ActionResult ValidCustomer(Customer customer)
            {
                Thread.Sleep(2000);
                if (!ModelState.IsValid)
                {
                    //return Json(new { vd = false, pv = this.RenderPartialViewToString("_Form", customer) });
                    return Json(new { vd = false, pv = this.RenderPartialViewToString("CustomerView", customer) });
                }
                return Json(new { vd = true, pv = this.RenderPartialViewToString("_Success", customer) });
            }
        }
    }
     

    当第二次提交错误信息时,不会跳转:
    3.4

      总结

    当涉及到表单异步提交的:
    1、优先考虑使用MVC自带的Ajax.BeginForm()方法,较快。
    2、其次考虑"jQuery+Html.BeginForm()方式",较慢,因为需要等待Html.BeginForm()提交。

    当涉及不到表单,只涉及部分属性异步提交的:
    1、优先考虑“MVC验证08-jQuery异步验证”:通过jquery,返回字符串,并把错误信息精确显示到指定html元素。
    2、其次考虑本篇的"通过jquery,返回json字符串,json字符串中包含部分视图及验证信息"。

  • 相关阅读:
    HDU 5938 Four Operations 【贪心】(2016年中国大学生程序设计竞赛(杭州))
    HDU 5935 Car 【模拟】 (2016年中国大学生程序设计竞赛(杭州))
    HDU 5934 Bomb 【图论缩点】(2016年中国大学生程序设计竞赛(杭州))
    HDU 5933 ArcSoft's Office Rearrangement 【模拟】(2016年中国大学生程序设计竞赛(杭州))
    HDU 5929 Basic Data Structure 【模拟】 (2016CCPC东北地区大学生程序设计竞赛)
    【转】LaTeX 符号命令大全
    HDU 5922 Minimum’s Revenge 【模拟】 (2016CCPC东北地区大学生程序设计竞赛)
    HDU 5927 Auxiliary Set 【DFS+树】(2016CCPC东北地区大学生程序设计竞赛)
    数据结构之稀疏矩阵
    C++中引用(&)的用法和应用实例
  • 原文地址:https://www.cnblogs.com/darrenji/p/3585644.html
Copyright © 2011-2022 走看看