MVC3学习第七章 排山倒海第三变----利用MVC3实现数据验证以及完成用户数据的编辑
本章学习内容
1.用DataAnnotations实现模型验证
2.实现用户数据的编辑实现(ActionResult.ViewResult)
1.用DataAnnotations实现模型验证
上一章中我们已经完成了用的添加和列表操作,对于一个完整的增删改查来说我们还缺少编辑和删除,这在本章后续会完成。我们先来回顾一下前文的添加,输入数据,入库保存,很简单的操作,有没有发现有什么不足的地方?或许聪明的你已经发现了,我们虽然保存了用户信息,但明显信息验证不够健壮,甚至连最起码的是否必须输入都没有验证,或许你想到了这时候可以去页面写验证的js,又或者你觉得此时缺少了webform下的RequiredFieldValidator和CustomValidator使得mvc的验证不太方便,其实,mvc的验证远比这些验证方式更加灵活好用,接下来我们就为我们的UserInfo加入表单验证。
首先,罗列一下我们要达到的验证效果:
1).所有数据必须输入,除了邮箱以外;
2).密码应是3-8个字符
3).邮箱和电话的格式应该正确
先就这么多吧,没有用到的验证我们之后也会讲到,打开Models/UserInfo,修改完整代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.ComponentModel.DataAnnotations; namespace MyShopTest.Models { public class UserInfo { [ScaffoldColumn(false)] public int Id { get; set; } [Display(Name="用户名")] [Required(ErrorMessage="必须输入用户名!")] public string UserName { get; set; } [Display(Name = "密码")] [Required(ErrorMessage = "必须输入密码!")] [StringLength(8, MinimumLength = 3, ErrorMessage = "密码长度必须是3到8个字符之间")] public string UserPwd { get; set; } [Display(Name = "电话")] [Required(ErrorMessage = "必须输入电话!")] [RegularExpression(@"((\d{11})|^((\d{7,8})|(\d{4}|\d{3})-(\d{7,8})|(\d{4}|\d{3})-(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1})|(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1}))$)",ErrorMessage="电话格式不正确")] public string Phone { get; set; } [Display(Name = "邮箱")] [RegularExpression(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*", ErrorMessage = "邮箱格式不正确")] public string Email { get; set; } private DateTime addTime; [Display(Name="注册时间")] [Required()] public DateTime AddTime { get { if (addTime == null) { return DateTime.Now; } return addTime; } set { addTime = value; } } } }
我们来解释一下这些属性头部的验证代码:
- Required 必须 – 表示这个属性是必须提供内容的字段
- Display 显示名 – 定义表单字段的提示名称(也可以写成DisplayName("要显示的名字"))
- StringLength 字符串长度 – 定义字符串类型的属性的最大长度
- Range 范围 – 为数字类型的属性提供最大值和最小值,此处没有用到
- Bind 绑定 – 列出在将请求参数绑定到模型的时候,包含和不包含的字段,此处没有用到,一般写在实体某些类名的头部,格式如[Bind(Exclude = "OrderId")]
- ScaffoldColumn 支架列 - 在编辑表单的时候,需要隐藏起来的的字符,主键会自动隐藏,这里可以不用为Id写,此处只是为了理解
- RegularExpression,自定义正则验证
重新编译项目,修改我们的Create视图,以便应用这些验证,修改UserInfo/Create.cshtml,完整代码如下:
@model MyShopTest.Models.UserInfo @{ ViewBag.Title = "添加用户"; } <h2> 添加用户</h2> <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> @using (Html.BeginForm()) { <fieldset> <legend>添加用户</legend> <div class="editor-label"> @Html.LabelFor(model => model.UserName) </div> <div class="editor-field"> @Html.EditorFor(model => model.UserName) @Html.ValidationMessageFor(model => model.UserName) </div> <div class="editor-label"> @Html.LabelFor(model => model.UserPwd) </div> <div class="editor-field"> @Html.PasswordFor(model => model.UserPwd) @Html.ValidationMessageFor(model => model.UserPwd) </div> <div class="editor-label"> @Html.LabelFor(model => model.Phone) </div> <div class="editor-field"> @Html.EditorFor(model => model.Phone) @Html.ValidationMessageFor(model => model.Phone) </div> <div class="editor-label"> @Html.LabelFor(model => model.Email) </div> <div class="editor-field"> @Html.EditorFor(model => model.Email) @Html.ValidationMessageFor(model => model.Email) <input type="hidden" name="AddTime" value="@DateTime.Now"/> </div> <p> <input type="submit" value="添加" /> </p> </fieldset> } <div> <a href="Index">返回列表</a> </div>
我们来解释一下这个修改后的Create视图我们不认识的标签,
@Html.LabelFor(model => model.UserName)等类似的代码
之前我们直接写的是“用户名”,“密码”等文字,现在换成了这一段代码, 它的意思是使用model.UserName的Display信息显示在此处,再看看我们的UserName属性的代码
[Display(Name="用户名")] [Required(ErrorMessage="必须输入用户名!")] public string UserName { get; set; }
此时使用@Html.LabelFor(model => model.UserName)就会显示用户名
再来看下一段代码:
@Html.EditorFor(model => model.UserName)
如同上一个的解释,不过此处的意思是输出一个model.UserName的文本框,之前我们是手写的文本框,此处也罗列一下:
<input type="text" name="UserName" />
两者实现的功能基本一样,之所以说基本,是因为还有不一样的,原因是使用@Html.EditorFor配合紧接着要说的代码段,可以实现自动验证
@Html.ValidationMessageFor(model => model.UserPwd)
这段代码就是执行我们的那些验证规则并且输出验证信息了,请注意此时只有使用@Html.EditFor或者其他@Html.输出的html标签,才能搭配输出验证信息,如果是我们自己手写的文本框则不可以。
好了,我们已经编写好了验证规则,我们来看一下效果,重新编译项目,在首页点击用户管理
报错了,恭喜你,修改差不多成功了。为什么会报错呢?因为我们修改了实体规则,这和数据库里现有的字段规则不符,所以报错了,要解决这个错误我们有两种方法,一是手动修改数据库字段的规则和实体一致,二是重新根据此实体来创建数据库。为了简便测试,我们直接删除掉数据库里现有的MyShop数据库,让系统重新创建,当然如果有很多数据时我们进行此操作很明显很危险,不过目前只是测试,所以不用考虑。
重新运行程序,进入用户管理
点击添加用户,输入一些错误信息,点击添加,看一下验证效果:
我们期望的验证已经基本都达到了,添加符合验证的信息,点击添加
操作成功。
2.实现用户数据的编辑实现
现在我们来实现用户的修改功能
还是一一样,我们先来创建对应的用户修改的Action,修改UserInfoController.cs的完整代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using MyShopTest.Models; using System.Data; namespace MyShopTest.Controllers { public class UserInfoController : Controller { //数据访问 private MyShopDataEntities db = new MyShopDataEntities(); /// <summary> /// 用户列表Action /// </summary> /// <returns></returns> public ActionResult Index() { var users = db.UserInfos.ToList(); return View(users); } /// <summary> /// 添加用户页面展示 /// </summary> /// <returns></returns> public ActionResult Create() { return View(); } /// <summary> /// 添加用户处理 /// </summary> /// <returns></returns> [HttpPost] public ActionResult Create(UserInfo user) { db.UserInfos.Add(user); db.SaveChanges(); return RedirectToAction("Index"); } /// <summary> /// 编辑用户页面展示 /// </summary> /// <returns></returns> public ActionResult Edit(int id) { var user = db.UserInfos.Find(id); return View(user); } /// <summary> /// 编辑用户处理 /// </summary> /// <returns></returns> [HttpPost] public ActionResult Edit(UserInfo user) { try { if (ModelState.IsValid) { db.Entry(user).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } else { throw new Exception(); } } catch (Exception) { ModelState.AddModelError("", "更改失败"); } return View(user); } } }
我们来解析一下新增Edit代码的新知识,db.UserInfos.Find(id)查找某个Id的用户,之后返回给视图,作用是在视图页面赋值;
ModelState.IsValid是用来验证当前模型实例是否有效的,说的更通俗一点,就是是否通过了字段合法性检查的验证,通过了则返回true,否则返回false;db.Entry(user).State = EntityState.Modified;意思是表名当前对象已经更新,但是还没保存,db.SaveChanges();执行数据库更改,如果一切操作顺利,则返回列表页,如果没有通过验证,我们来抛出一个异常,如果期间更新出错,也进入异常处理,异常处理里ModelState.AddModelError("", "更改失败");就是为当前模型添加错误信息,除了异常则将当前模型返回此Action对应的视图,视图里如果有 @Html.ValidationSummary(true)语句,则会输出错误信息,这在稍后的修改视图里大家就可以看到。
现在添加Edit视图,修改完整代码如下
@model MyShopTest.Models.UserInfo @{ ViewBag.Title = "用户编辑"; } <h2>用户编辑</h2> <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> @using (Html.BeginForm()) { @Html.ValidationSummary(true) <fieldset> <legend>用户编辑</legend> <div class="editor-label"> @Html.LabelFor(model => model.UserName) </div> <div class="editor-field"> @Html.EditorFor(model => model.UserName) @Html.ValidationMessageFor(model => model.UserName) </div> <div class="editor-label"> @Html.LabelFor(model => model.UserPwd) </div> <div class="editor-field"> @Html.PasswordFor(model => model.UserPwd) @Html.ValidationMessageFor(model => model.UserPwd) </div> <div class="editor-label"> @Html.LabelFor(model => model.Phone) </div> <div class="editor-field"> @Html.EditorFor(model => model.Phone) @Html.ValidationMessageFor(model => model.Phone) </div> <div class="editor-label"> @Html.LabelFor(model => model.Email) </div> <div class="editor-field"> @Html.EditorFor(model => model.Email) @Html.ValidationMessageFor(model => model.Email) </div> <p> <input type="submit" value="修改" /> </p> </fieldset> } <div> @Html.ActionLink("返回列表", "Index") </div>
代码应该都说过了,没有新知识, @Html.ValidationSummary(true)是用来显示Model错误信息的。
除了@Html.ActionLink,在此也解释一下,@Html.ActionLink是用来创建链接的,它也有很多重载的方法,简单说明几个:
一 Html.ActionLink("linkText","actionName")
该重载的第一个参数是该链接要显示的文字,第二个参数是对应的控制器的方法,
默认控制器为当前页面的控制器,如果当前页面的控制器为Products,则 Html.ActionLink("detail","Detail")
则会生成 <a href="/Products/Detail">all</a>
二 Html.ActionLink("linkText","actionName","controlName")
该重载比第一个重载多了一个参数,他指定了控制器的名称,
如Html.ActionLink("detail","Detail","Products")则会生成
<a href="Products/Detail">all</a>
三 Html.ActionLik("linkText","actionName",routeValues)
routeValue可以向action传递参数,如Html.ActionLink("detail","Detail",new { id=1})
会生成 <a href="Products/Detail/1">detail</a>,
此处假设当前的控制器是Products.
详细请参考http://blog.csdn.net/jingmeifeng/article/details/7792151
现在我们在列表页加上编辑链接,修改Index.cshtml完整代码如下:
@model IEnumerable<MyShopTest.Models.UserInfo> @{ ViewBag.Title = "用户列表"; } <h2> 用户列表</h2> <p> <a href="/UserInfo/Create">添加用户</a> </p> <table> <tr> <th> 用户名 </th> <th> 电话 </th> <th> 邮箱 </th> <th> 注册时间 </th> <th> 操作 </th> </tr> @foreach (var item in Model) { <tr> <td> @item.UserName </td> <td> @item.Phone </td> <td> @item.Email </td> <td> @item.AddTime </td> <td> @Html.ActionLink("编辑", "Edit", new { id=item.Id}) </td> </tr> } </table>
重新运行项目,用户管理,点击编辑,
结果如下:
修改任意数据,点击修改:
提示错误信息,错误原因是更新时获取不到Id,我们的编辑页面还缺少一个保存当前修改信息的Id的隐藏域。加入下面的隐藏域代码到Edit.cshtml,修改后的完整代码是:
@model MyShopTest.Models.UserInfo @{ ViewBag.Title = "用户编辑"; } <h2>用户编辑</h2> <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> @using (Html.BeginForm()) { @Html.ValidationSummary(true) <fieldset> <legend>用户编辑</legend> @Html.HiddenFor(model => model.Id) <div class="editor-label"> @Html.LabelFor(model => model.UserName) </div> <div class="editor-field"> @Html.EditorFor(model => model.UserName) @Html.ValidationMessageFor(model => model.UserName) </div> <div class="editor-label"> @Html.LabelFor(model => model.UserPwd) </div> <div class="editor-field"> @Html.PasswordFor(model => model.UserPwd) @Html.ValidationMessageFor(model => model.UserPwd) </div> <div class="editor-label"> @Html.LabelFor(model => model.Phone) </div> <div class="editor-field"> @Html.EditorFor(model => model.Phone) @Html.ValidationMessageFor(model => model.Phone) </div> <div class="editor-label"> @Html.LabelFor(model => model.Email) </div> <div class="editor-field"> @Html.EditorFor(model => model.Email) @Html.ValidationMessageFor(model => model.Email) </div> @Html.HiddenFor(model => model.AddTime) <p> <input type="submit" value="修改" /> </p> </fieldset> } <div> @Html.ActionLink("返回列表", "Index") </div>
不用生成,直接刷新页面,重新修改数据提交
提交后结果
用户编辑完成