.NET MVC 学习笔记(五)—— Data Validation
在实际应用中,我们需要对数据进行增查改删业务,在添加和修改过程中,无论你编写什么样的网页程序,都需要对用户的数据进行验证,以确数据的有效性和完整性。目前我们可以使用Bootstrap Validation对画面进行前端验证,我们先来看一下这种验证方式。
一、Bootstrap Validation
使用方式:
1. 引用js库
<link href="~/bower_components/bootstrap-validation/css/bootstrapValidator.min.css" rel="stylesheet" />
<script src="~/bower_components/bootstrap-validation/js/bootstrapValidator.min.js"></script>
2. 在页面中对validation进行初始化
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
×
</button>
<h4 class="modal-title" id="myModalLabel">
新增
</h4>
</div>
<div class="modal-body">
<div class="row clearfix">
@using (Html.BeginForm("Save", "Client", FormMethod.Post, new { @id = "frmClient" }))
{
<div class="form-group">
@Html.Hidden("Id")
<div class="row edit-field-div">
<div class="col-sm-3 input-label">
<label for="CardNo" class="control-label">卡号 <span style="color:red">*</span></label>
</div>
<div class="col-sm-8">
<input type="text" class="form-control" name="CardNo" id="CardNo">
</div>
</div>
<div class="row edit-field-div">
<div class="col-sm-3 input-label">
<label for="UserName" class="control-label">会员名 <span style="color:red">*</span></label>
</div>
<div class="col-sm-8">
<input type="text" class="form-control" autocomplete="off" name="UserName" id="UserName">
</div>
</div>
<div class="row edit-field-div">
<div class="col-sm-3 input-label">
<label for="Sex" class="control-label">性别 <span style="color:red">*</span></label>
</div>
<div class="col-sm-8">
@Html.DropDownListFor(model => model.Sex, ViewBag.GenderList as IEnumerable<SelectListItem>, new { @class = "form-control textbox input-sm" })
</div>
</div>
<div class="row edit-field-div">
<div class="col-sm-3 input-label">
<label for="BirthdateText" class="control-label">出生日期 <span style="color:red">*</span></label>
</div>
<div class="col-sm-8">
<div class="input-group date">
<div class="input-group-addon">
<i class="fa fa-calendar"></i>
</div>
@Html.TextBoxFor(model => model.BirthdateText, new { @class = "form-control datepicker", @type = "text", @autocomplete = "off" })
</div>
</div>
</div>
<div class="row edit-field-div">
<div class="col-sm-3 input-label">
<label for="Phone" class="control-label">手机号 <span style="color:red">*</span></label>
</div>
<div class="col-sm-8">
<input type="text" id="Phone" name="Phone" autocomplete="off" class="form-control input-sm span3">
</div>
</div>
<div class="row edit-field-div">
<div class="col-sm-3 input-label">
<label for="Address" class="control-label">地址</label>
</div>
<div class="col-sm-8">
<input type="text" id="Address" name="Address" autocomplete="off" class="form-control input-sm span3">
</div>
</div>
<div class="row edit-field-div">
<div class="col-sm-3 input-label">
<label for="Score" class="control-label">积分</label>
</div>
<div class="col-sm-8">
<input type="text" id="Score" name="Score" autocomplete="off" class="form-control input-sm span3">
</div>
</div>
<div class="row edit-field-div">
<div class="col-sm-3 input-label">
<label for="GradeCode" class="control-label">等级</label>
</div>
<div class="col-sm-8">
@Html.DropDownListFor(model => model.GradeCode, ViewBag.GradeList as IEnumerable<SelectListItem>, new { @class = "form-control textbox input-sm" })
</div>
</div>
</div>
}
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">
关闭
</button>
<button type="button" class="btn btn-primary" id="btnSaveClient">
保存
</button>
</div>
</div>
$('#frm').bootstrapValidator({
message: 'This value is not valid',
feedbackIcons: {
valid: 'glyphicon glyphicon-ok',
invalid: 'glyphicon glyphicon-remove',
validating: 'glyphicon glyphicon-refresh'
},
fields: {
CardNo: {
verbose: false,
validators: {
notEmpty: {
message: '卡号不能为空'
},
remote: {
type: 'POST',
url: '@Url.Content("~/Client/CheckCardNo")',
dataType: 'json',
data: {
ClientId: function () {
return $('#Id').val()
},
CardNo: function () {
return $('#CardNo').val()
}
},
message: '此卡号已存在',
delay: 200
}
}
},
UserName: {
validators: {
notEmpty: {
message: '会员名不能为空'
}
}
},
Phone: {
validators: {
notEmpty: {
message: '手机号码不能为空'
},
regexp: {
regexp: /^1[3|5|8]{1}[0-9]{9}$/,
message: '请输入正确的手机号码'
}
}
},
Score: {
validators: {
regexp: {
regexp: /^[\d]+$/,
message: '积分必须为数字'
}
}
}
}
});
如上代码所示,对frm表单进行验证初始化,CardNo,UserName等为控件的name属性
运行效果如下:
其中,对CardNo有重复性check,此时需要使用remote进行验证,后台代码如下:
/// <summary>
/// 检查CardNo
/// </summary>
/// <param name="ClientId"></param>
/// <param name="CardNo"></param>
/// <returns></returns>
public JsonResult CheckCardNo(String ClientId, String CardNo)
{
try
{
// 检索条件
ClientFilter filter = new ClientFilter();
filter.CardNo = CardNo;
List<ClientDomain> clients = ClientBiz.GetDomainByExactFilter(filter) as List<ClientDomain>;
ValidaResult resObj = new ValidaResult();
resObj.valid = true;
if (String.IsNullOrEmpty(ClientId) && clients.Count > 0)
{
resObj.valid = false;
}
else if (!String.IsNullOrEmpty(ClientId))
{
if (clients.Count > 1)
{
resObj.valid = false;
}
else if (clients.Count == 1 && !clients[0].Id.Equals(ClientId))
{
resObj.valid = false;
}
}
return Json(resObj, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
Log.SaveException(ex);
return new JsonResult()
{
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Data = new { ResultTitle = Constant.Result_Title_Error, ResultMessage = ex.Message }
};
}
}
/// <summary>
/// Valid 结果
/// </summary>
[Serializable]
[DataContract]
public class ValidaResult
{
[DataMember]
public Boolean valid { get; set; }
}
验证效果如下:
regexp:是对字段进行正则表达式验证,可以根据自己的需要进行相应的验证。
还有密码确认密码验证
//密码确认
edit_passwd1: {
message: '密码确认验证失败',
validators: {
notEmpty: {
message: '密码确认不能为空'
},
identical: {
field: 'edit_passwd',
message: '两次密码不相同'
}
}
}
长度验证
stringLength: {
min: 5,
max: 128,
message: '显示名长度必须在6到18位之间'
}
等等
在提交画面变更时,需要进行验证
//开启验证
$('#frm').data('bootstrapValidator').validate();
if ($('#frm').bootstrapValidator('validate').has('.has-error').length != 0) {
return;
}
或者
var flag = $("#frm").data(“bootstrapValidator”).isValid();
验证通过之后进行页面数据提交
在画面初始化的时候,如果上次画面关闭前有验证消息,再次打开或许会出现验证消息依然存在的情况,这时候需要重置所有验证
$("frm").data("bootstrapValidator").resetForm();
以上,就是相关前端验证的内容。
========================================================================
是不是到这里就结束了,一开始我也以为是的,源自于我对用户的信任,可是事实上大部分用户也完全值得我们的信任,但是作为开发者,我们不能允许有异常发生。
以此为例:
请看下面的画面
这是对【会员名】的验证
UserName: {
validators: {
notEmpty: {
message: '会员名不能为空'
}
}
}
当我们修改会员名,设置为空时
看起来一切正常,只有输入会员名我们才能提交,但是当我们输入会员名,并点击【保存】
我们这时选择在代码中打入断点
此时我们看到,代码已经进行了正常的前端Validation验证,并且已经验证通过了,那么如果我们此时在调试窗口手动的去修改画面的值,那么验证将不再有效
可以看到,手动的将【会员名】改成了空值,运行结束后看最终结果值
此时【会员名】变成空了。
那么,我们就需要考虑一个问题,如何避免这个问题。
答案很简单,进行双重验证,客户端和服务器端都进行验证,由此我们还需要对模型进行验证。验证方法如下:
MVC 模型注解验证
在对应的实体类的字段上加注解,如
/// <summary>
/// UserName
/// </summary>
[DataMember]
[Required(ErrorMessage = "会员名不能空")]
public String UserName
{
get
{
return this.userName;
}
set
{
this.userName = value;
}
}
<div class="row edit-field-div">
<div class="col-sm-3 input-label">
<label for="UserName" class="control-label">会员名 <span style="color:red">*</span></label>
</div>
<div class="col-sm-8">
@Html.TextBoxFor(model => model.UserName, new { @class = "form-control", @autocomplete = "off" })
@Html.ValidationMessageFor(model => model.UserName,"", new { @style="color:red"})
</div>
// 保存信息
$('#frm').submit();
Form 表单在提交的时候会做后台验证,也就是对实体类的验证
其他验证如下:
转自:https://www.cnblogs.com/dianshen520/p/4349229.html
1、模型注解,用来检查表单中元素的合法性,效果类似于前面学过的 formvalidate.js插件
命名空间: using System.ComponentModel; 下面存储了
1、DisplayName():标记属性要显示的名称 配合视图上面的 @Html.DisplayNameFor(c=>c.属性名称)来显示
命名空间: using System.ComponentModel.DataAnnotations; 下面存储了
1、Required :用来做非空验证检查,特点:在非字符串类型属性上会自动加上 Required 特性标签,如果是字符串类型必须手动添加
2、StringLength :用来检查字符串类型的属性的长度验证
3、Range:用来检查数值(int,decimal,double,float)类型的属性的范围验证
4、Compare:用来比较两个属性的值是否一致,一般用于确认密码属性上
5、[RegularExpression("\\d+", ErrorMessage="年龄必须是一个数值")]:约束此属性的值必须是一个数值
命名空间: using System.ComponentModel.DataAnnotations; 下面存储了
1、Remote("请求的action名称","控制器名称",HttpMethod = "post/get(一般推荐使用post请求来防止ajax的缓存)",ErrorMessage = "提示文本"):会发出一个ajax请求到某个控制器中的某个 action进行检查操作,如果存在则返回字符串的 false (注意不能使用大写的False,如果使用了则不能正常调用)
表示此值已经存在不能再使用,否则返回true(注意不能使用大写的True,如果使用了则不能正常调用) 表示可以使用
[System.Web.Mvc.Remote("CheckUserName" //代表的是请求的action名称
,"Home" //代表控制器名称
,HttpMethod="post" //表示发出post的ajax异步请求,主要是为了防止ajax请求的缓存,如果使用get可能出现结果不准确
,ErrorMessage="此值已经被使用,请重新跟换") //表示如果服务器返回的是false字符串,则提醒用户
]
eg:
/// <summary>
/// 员工信息
/// </summary>
public class StaffInfo
{
public virtual int StaffInfoId { get; set; } [Required]
[Display(Name = "登录账号")]
public virtual string LogID { get; set; } [StringLength(10, MinimumLength = 4, ErrorMessage = "{0}的长度必须大于{2}个字符并小于{1}个字符")]
[Display(Name = "密码")]
public virtual string LogPassword { get; set; } [StringLength(10, ErrorMessage = "{0}的长度不能大于{1}个字符")]
[Display(Name = "姓名")]
public virtual string RealName { get; set; } [Display(Name = "出生日期")]
//[Range(typeof(DateTime), "2011-12-31", "1950-1-1", ErrorMessage = "{0}的范围是{1}到{2}")]
public virtual DateTime Birthday { get; set; } [RegularExpression(@"\d{17}[\d|X]|\d{15}", ErrorMessage = "{0}的格式不正确")]
[Display(Name = "身份证号码")]
public virtual string IdentityNo { get; set; } [RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}", ErrorMessage = "{0}的格式不正确")]
[Display(Name = "邮箱")]
public virtual string Email { get; set; } [Display(Name = "逻辑删除标识")]
public virtual int IsLogicDelete { get; set; }
}
总结如下:
使用Required 进行页面元素的合法性验证步骤:
1、在实体属性上打上[Required(ErrorMessage="Name属性非空")]
2、在视图页面上 利用@Html.TextBoxFor(c=>c.Name) :用来生产文本框,同时给文本框架加上 data-val="true" data-val-*......
3、在视图页面上 利用@Html.ValidationMessageFor(c=>c.Name) 用来显示提示文本的
4、引用三个js脚本
<script src="~/Scripts/jquery-1.7.1.js"></script>
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
5、注意:元素必须放到表单中 <form> 标签才能起验证作用
6、检查MVC网站跟目录下面的web.config中的<appSettings>节点中的两个节点
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
的值必须为true,才能起验证作用,否则任何一个关闭都不起作用
7、最终表单的提交需要用form的submit()方法来进行验证(自己验证的结果)
什么叫做非嵌入式脚本(非侵入式脚本)~/Scripts/jquery.validate.unobtrusive.js
没有在视图上写一个验证的代码就完成表单元素的验证功能,仅仅只是利用了相关的脚本来完成,这个就叫做非嵌入式
OK,这样前端和后端的基本验证就做完了。
ps:开发过程中,可以适当考虑一下前端验证存在的必要性。
// REMOTE
remote验证,保存时会有需要两次点击保存按钮,解决方案:
$('#myClientModal').on('shown.bs.modal', function (e) {
$('#frmClient').data('bootstrapValidator').resetForm(false);
if (!isNull($("#Id").val())) {
$('html').one('mouseover', function () {
//每次弹框弹起后都会进行一次校验,而且只校验一次
$('#frmClient').data("bootstrapValidator").validate();
});
}
});