ASP.NET MVC验证 - jQuery异步验证

时间:2022-05-09 18:03:19

本文主要体验通过jQuery异步验证。

在很多的教材和案例中,MVC验证都是通过提交表单进行的。通过提交表单,可以很容易获得验证出错信息。因为,无论是客户端验证还是服务端验证,总能找到与Model属性或验证特性对应的html元素和属性,并把错误信息显示出来。可是,在实际项目中,经常会遇到需要异步提交的情况。那么,如何把服务端的验证错误信息传递给前端视图呢?

□ 思路

1、服务端的验证错误信息是可以收集起来的以json形式传递个视图的。
2、服务端把错误信息存放在一个字典集合Dictionary<string, object>,让key是属性名,value是错误信息。
3、前台视图中,显示错误信息的元素id,比方说是Err_Name,当遍历从服务端传来的字典集合时,取出key,然后把错误信息动态赋值给$('#Err_' + key)。

View model

using System;
using System.ComponentModel.DataAnnotations;
 
namespace DataAnnotationAjax.Models
{
    public class Student
    {
        public int Id { get; set; }
 
        [Required(ErrorMessage = "姓名为必填项")]
        [Display(Name = "姓名")]
        public string Name { get; set; }
 
        [Required(ErrorMessage = "分数是必选项")]
        [Range(60, 100, ErrorMessage = "分数必须在60和100之间")]
        [Display(Name = "分数")]
        public int Score { get; set; }
 
        [Display(Name = "招收日期")]
        public DateTime Enrollment { get; set; }
    }
}

模拟一个仓储层,负责数据的初始化、添加和显示

using System;
using System.Collections.Generic;
using DataAnnotationAjax.Models;
 
namespace DataAnnotationAjax.Service
{
    public static class StudentRepository
    {
        private static int _idSeed = 1;
        private static readonly List<Student>  _students = new List<Student>();
 
        static StudentRepository()
        {
            Random rand = new Random();
            for (int i = 0; i < 3; i++)
            {
                var student = new Student();
                int id = _idSeed++;
                student.Id = id;
                student.Name = "姓名" + id.ToString();
                student.Score = (60 + Convert.ToInt16(rand.NextDouble()*40));
                student.Enrollment = DateTime.Now;
                _students.Add(student);
            }
        }
 
        public static void AddStudent(Student stu)
        {
            stu.Id = _idSeed++;
            stu.Enrollment = DateTime.Now;
            _students.Add(stu);
        }
 
        public static List<Student> GetStudents()
        {
            return _students;
        }
    }
}
 

BaseController

前台视图为了显示错误信息等,需要控制器传来的json可能包含如下构成:

1、是否验证通过:这个bool值很容易通过ModelState拿到。
2、错误信息的字典集合:每个控制器都有可能用到,可以把获取错误信息的字典集合方法放到一个基控制器中去。
3、再加一个福利:有时希望部分视图以字符串形式传递给某一视图,那就把根据视图名称和model返回视图字符串的方法也放到基控制器中。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc; namespace DataAnnotationAjax.Controllers
{
public class BaseController : Controller
{
/// <summary>
/// 把部分视图转换成string
/// </summary>
/// <param name="viewName">部分视图名称</param>
/// <param name="model">view model</param>
/// <returns>部分视图字符串</returns>
public string RenderPartialViewToString(string viewName, object model)
{
ViewData.Model = model;
using (var sw = new StringWriter())
{
//根据部分视图名称+ControllerContext获得ViewEngineResult
//ViewEngineResult中有View属性
var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName); //创建ViewContext对象实例
var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw); //把ViewEngineResult中的视图渲染到StringWriter实例中
viewResult.View.Render(viewContext,sw); //获取视图string
return sw.GetStringBuilder().ToString();
}
} /// <summary>
/// 获取ModelState中的错误信息,以字典集合的形式返回
/// </summary>
/// <returns></returns>
public Dictionary<string, object> GetErrorFromModelState()
{
var errors = new Dictionary<string, object>();
foreach (var key in ModelState.Keys)
{
if (ModelState[key].Errors.Count > 0)
{
errors[key] = ModelState[key].Errors;
}
}
return errors;
}
}
}

HomeController

HomeController做了:

1、显示一个异步提交的视图Index.cshtml
2、完成了验证通过情况下的数据添加。
3、不管是否验证通过,都要返回json字符串。

using System.Web.Mvc;
using DataAnnotationAjax.Models;
using DataAnnotationAjax.Service;
 
namespace DataAnnotationAjax.Controllers
{
    public class HomeController : BaseController
    {
        public ActionResult Index()
        {
            return View(StudentRepository.GetStudents());
        }
 
        [HttpPost]
        public ActionResult AddStudent()
        {
            var student = new Student();
            var valid = TryUpdateModel(student);
            string studentPartialViewHtml = string.Empty;
            if (valid)
            {
                StudentRepository.AddStudent(student);
                var students = StudentRepository.GetStudents();
                studentPartialViewHtml = RenderPartialViewToString("Students", students);
            }
            return Json(new {Valid = valid, Errors = GetErrorFromModelState(), StudentsPartial = studentPartialViewHtml});
        }
    }
}
 

部分视图Students.cshtml

@model IEnumerable<DataAnnotationAjax.Models.Student>

<table>
<tr>
<th>
@Html.DisplayNameFor(model => model.Name)
</th>
<th>
@Html.DisplayNameFor(model => model.Score)
</th>
<th>
@Html.DisplayNameFor(model => model.Enrollment)
</th>
</tr> @foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Score)
</td>
<td>
@item.Enrollment.ToString("yy-MM-dd")
</td>
</tr>
} </table>

Index.cshtml异步提交的界面

@model IEnumerable<DataAnnotationAjax.Models.Student>
 
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<style type="text/css">
    .errormsg {
        color: red;
    }
</style>
 
<div>
<table cellpadding="0px">
    <tr>
        <td>姓名</td>
        <td><input type="text" id="Name" /></td>
    </tr>
    <tr>
        <td></td><td colspan="2" 
              id="Err_Name" class="errormsg"></td>
    </tr>
    <tr>
        <td>分数</td>
        <td><input type="text" id="Score" /></td>
        <td>
            <button id="btnAddStudent">添加学生</button>
            <button id="btnClear">清空</button>
        </td>
    </tr>
    <tr>
        <td></td><td colspan="2" 
              id="Err_Score" class="errormsg"></td>
    </tr>
</table>
</div>
 
<div id="divStudent">
    @{Html.RenderPartial("Students",Model);}
</div>
 
@section scripts
{
    <script type="text/javascript">
        $(function() {
            $('#btnAddStudent').click(function() {
                var data = {
                    Name: $.trim($('#Name').val()),
                    Score: $.trim($('#Score').val())
                };
 
                $.ajax({
                    cache: false,
                    type: "POST",
                    url: '@Url.Action("AddStudent", "Home")',
                    data: data,
                    dataType: "json",
                    success: function(data) {
                        if (data.Valid) {
                            $('#divStudent').html(data.StudentsPartial);
                            $('input').val("");
                            return;
                        }
                        $.each(data.Errors, function(key, value) {
                            if (value != null) {
                                $('#Err_' + key).html(value[value.length - 1].ErrorMessage);
                            }
                        });
                    },
                    error: function(xhr) {
                        alert(xhr.responseText);
                        alert("数据没有能提交到服务器!");
                    }
                });
            });
 
            $('#btnClear').click(function() {
                $('.errormsg').html("");
                $("input").val("");
            });
 
            $("input").keyup(function() {
                var $errorDiv = $("#Err_" + this.id);
                if ($errorDiv.html() !="") {
                    $errorDiv.html("");
                }
            });
        });
    </script>
}
 

几个关键点:
1、显示错误信息的元素id的命名有讲究的:Err_Name,Name与Model中的属性一致。
2、遍历服务端传来的错误信息字典集合时,对每个属性,即key,拿的是最近一次错误:$('#Err_' + key).html(value[value.length - 1].ErrorMessage)。

没有填写信息报错:

ASP.NET MVC验证 - jQuery异步验证

填写分数不在定义区间报错:

ASP.NET MVC验证 - jQuery异步验证

全部填写正确,验证通过,把部分视图以string形式返回并加载到页面区域中:

ASP.NET MVC验证 - jQuery异步验证