这篇文章发出来有段时间了不过看的人不多建议的人也少! 今天在首页看到有朋友把验证方案发到首页去了为了赚些评论我也在这边编辑下发到首页上给大家扔些砖!
一起源
在项目开发中数据有效性验证肯定是必须的那么在哪里验证呢!?怎么去验证呢? 针对web项目而言客户端验证+服务端的验证缺一不可客户端的脚本验证用于提高用户体验! 服务端的验证主要是为了数据的安全性合法性的验证! 但是我们在实施这两种验证的时候会发现有以下几个可能出现的问题
客户端代码验证写起来相对比较烦琐而且易出错!(主要是拼写错误例如js方法名拼写错误)
服务端的验证和客户端验证要保持一致性一样很烦琐(客户验证输入不能超过个字符那后服务器端也要相应的控制在个字符而且修改时需要将客户端和服务端同步修改提示信息前后端都要再输入一次!一个字烦)总觉得时间不应该花在这个上面!
验证与赋值一般情况下客户端提交的数据我们验证通过后把变量的值赋给实体或是相关的变量如果不通过有些时候我们有时候还会需要给变量或实体属性值加上一个默认值这个时候我们写的代码可能不避免的出现一些if的判断语句让代码显得不够简洁
其它的细节问题这里就不一一列举了我针对上面的问题自己做了一个下面的小组件!
二 思路说明
为了解决第一个问题和第二个问题我决定客户端的验证代码由服务端的验证代码直接生成页面中这样可以避免客户端的代码在编写上的错误!而服务端的程序代码每次被访问时通过验证状态开关来开启验证用户可以根据自己的业务逻辑来开启验证我先定义了一个验证容器ValidatorContainer和一个验证器Validator两个概念验证容器是指包括了一个或多个验证器对象的容器类验证器是针对每一条数据项所添加的验证信息类!
三调用说明
可能这一部分的内容可以让你更直观的了解这个验证组件的一些原理和信息
第一步先添加引用这个不用多说Validatordll引用进去(最下面附有项目的源码)
第二步代码都在下面了不用再说了
cs页面的代码:
using Validator;
using MyFrameworkUtility;
public partial class _Default : SystemWebUIPage
{
protected ValidatorContainer vc;
protected void Page_Load(object sender EventArgs e)
{
//是否开启服务端验证
bool isStartCheck = false;
if (Request[command] == postBack)
{
/*
* 此处的bool值主要控制验证容器处在何种状态
* 状态false只注册客户端代码不实施服务端的验证
* 状态true验证客户端的数据并生成客户端代码字符串
* */
isStartCheck = true;
}
//初始化验证容器的集合并传入isStartCheck来控制验证的开启
vc = new ValidatorContainer(isStartCheck);
//设置当前的验证组(些处可以根据你自己的页面逻辑来选择验证不设置为验证所有)
vcGroupName = A;
//获取并验证客户端的数据
int month = vcAdd(Validator<int>Init(MyRequestGetInt(monthTextBox)))//注意这一行我们需要把验证器放置到对应的容器中再进行验证
Integer(月份必须为到的整数)
Range(月份必须在到月之间)//验证数字和范围
SetGroupName(A)//设置其所属的验证组可以不设置(不设置会被分配到default组)
RegClientValidator(monthTextBox 请输入出生月 monthErrorSpan)//将验证代码注册到客户端页面(也可以选择不注册)
//参数说明参数monthTextBox表示html控件的id[参数]请输入出生月表示提示Div中的文字(该参数为重载)[参数]monthErrorSpan表示错误信息所显示的框(该参数为重载)
Retrun();//返回验证通过后的值否则返回默认值(此处返回的是);
string userName = vcAdd(Validator<string>Init(MyRequestGetString(userNameTextBox)))
Chinese(用户名必须为中文!)
Length(用户名必须在到个字以内!)
SetGroupName(A)
RegClientValidator(userNameTextBox)
Retrun(给你个默认值);
string rePostDateString = vcAdd(Validator<string>Init(MyRequestGetString(rePostDateTextBox)))
Date(日期格式不正确)
Range(DateTimeNow DateTimeMaxValue 重发日期不能少于当前日期!)//这个功能客户端的还没写
SetGroupName(A)
IsRequired(false)//设置其为非必填项
RegClientValidator(rePostDateTextBox这个是非必填项当然你如果要填的话我肯定是要验证的啦!)
Retrun();
//注册客户端代码到页面中这里必须要声明运行于服务端的Header或是Form才能进行注册
vcRenderClinetValidator();
//判断验证是否通过
if (isStartCheck)
{
if (vcIsAllPass)
{
//你页面要做的逻辑
}
}
}
}
aspx页面的调用代码:
<head>
<!这里加runat=server或是如现在的页面有一个 <form id=form runat=server>是runat=server的>
<title>验证器测试页</title>
</head>
<body >
<form id=form runat=server>
<div>
<br />
请输入出生月份<input type=text id=monthTextBox value=aa /><span id=monthErrorSpan >这里将用来显示验证反馈信息</span><br />
请输入用户昵称<input type=text id=userNameTextBox value=aa /><br />
更新日期<input type=text id=rePostDateTextBox value=aa /><br />
<input type=button id= value=统一验证 onclick=<%=vcChecked(A)%> />
<!上面这个地方就是调用的地方<%=vcChecked(A)%>会产生一个js调用函数
A代表要验证的组产生的这个调用的函数执行完会返回一个表单是否验证通过的bool值 >
</div>
</form>
</body>
</html>
上面这段代码就实现了客户端验证代码的注册及调用应该来说还是比较简单的!
上面的调用上都写了相关的注释如果有不明确的地方接着往下看
//是否开启服务端验证
bool isStartCheck = false;
if (Request[command] == postBack)
{
/*
* 此处的bool值主要控制验证容器处在何种状态
* 状态false只注册客户端代码不实施服务端的验证
* 状态true验证客户端的数据并生成客户端代码字符串
* */
isStartCheck = true;
}
//初始化验证容器的集合并传入isStartCheck来控制验证的开启
vc = new ValidatorContainer(isStartCheck);
//设置当前的验证组(些处可以根据你自己的页面逻辑来选择验证不设置为验证所有)
vcGroupName = A;
上面这段代码里面是初始化开关条件变量以及初始化验证器以及验证的组名这个地方组名其实是非必须的如查不添加这个组名组件会自动分
配到Default组中在new ValidatorContainer(isStartCheck)中的isStartCheck是用为启动验证的如果这个值是false的话那么
ValidatorContainer就只会生成客户端代码而不执行数据有效性验证!
接下来再看下面的代码
//获取并验证客户端的数据
int month = vcAdd(Validator<int>Init(MyRequestGetInt(monthTextBox)))//注意这一行我们需要把验证器放置到对应的容器中再进行验证
Integer(月份必须为到的整数)
Range(月份必须在到月之间)//验证数字和范围
SetGroupName(A)//设置其所属的验证组可以不设置(不设置会被分配到default组)
RegClientValidator(monthTextBox 请输入出生月 monthErrorSpan)//将验证代码注册到客户端页面(也可以选择不注册)
//参数说明参数monthTextBox表示html控件的id[参数]请输入出生月表示提示Div中的文字(该参数为重载)[参数]monthErrorSpan表示错误信息所显示的框(该参数为重载)
Retrun();//返回验证通过后的值否则返回默认值(此处返回的是);
这个里面我们可以看出来我们用vcAdd(Validator<T> v)方法来往验证器里面添加验证器特别注意的地方是
Validator本身具备了IntegerRange 等方法但是我们这里不可以直接调用如果直接调用在这里它的验证结果将不能被vc容监控到!
所以要特别注意
Validator<int>Init(MyRequestGetInt(monthTextBox))
这小段代码是为MyRequestGetInt(monthTextBox)这个整数做一个校验这个地方这个方法是另一个组件里的大家应该看得明白用
来获取客户端的一个提交值的如果不为整数的话默认为这个地方有点多余因为验证器里本来就有一个针对整数的验证了这个我们就先不
管它了反正我这个组件的思路是不相干的!
后面调用的IntegerRange 等方法就不多做解释了它们只是一个验证方法而已关键RegClientValidator方法这个方法有三个重载版本
RegClientValidator(string elementName);这个表示为html页面中id或是name为elementName的html控件添加js验证的脚本代码(注:这一步其实只做了一个脚本代码的生成并没有输出)
RegClientValidator(string elementNamestring tipString);这个重载表示为页面控件添加验证并在输入时添加提示信息提示信息的位置现在是
默认的在控件的上方(具体效果如图我会在以后的代码更新中会加入一个可以自定义显示的位置给用灵活的配置或是使用如图的默认)
如果不使用这个重载自然没有这个提示信息了!
然后讲第三个重载:RegClientValidator(string elementNamestring tipStringstring errspanName)
这个重载前两上参数都讲过了第三个参数其实是控制是否在自定义的html元素里面显示错误提示信息
我代码中monthErrorSpan其实就是图上红色的那块区域的html元素的id这样如果验证不通过信息将在红色区域中显示如果没有使用这个重载
我们会自动生成一个div来显示效果如下图:
注这个错误提示的内容就是在cs文件里面写的那些字符串在哪一下的验证中不通过就会显示相应的错误提示!
接下来看Retrurn(T tValue);
这个方法其实就是用来返回值的意思就是如果验证通过返回用户提交的数据给接收变量如果不通过将Retrun(T tValue)中定义的值赋给接收变量这个应该比较好理解如果你没有定义接收变量这个方法也可以省略~
然后再看这两个方法
SetGroupName(A)
IsRequired(false)//设置其为非必填项
上面那个SetGroupName(A)表示把该验证添加到A组中其实这个地方我要声明的是一般%甚至更多的页面的验证是不需要分组的当然这个也用不着我在这个地方只是为以演示这个分组功能才加上去的分组验证的情况有用过的朋友估计会明白我这个地方就不多说了不需要分组的就不用调用这个方法咯!
下面这个IsRequired(false)这个字面上应该也很好理解就是设置成非必填项当然如果用户输入了的话还是会有验证的!
好了上面对该解释的都解释了现在看最后的几代码
//注册客户端代码到页面中这里必须要声明运行于服务端的Header或是Form才能进行注册
vcRenderClinetValidator();
//判断验证是否通过
if (isStartCheck)
{
if (vcIsAllPass)
{
//你页面要做的逻辑
}
}
vcRenderClientValidator();这个方法做的工作是客户端验证生成的最后一步它负责把容器里所有的客户端验证代码生成后写入到html代码中~!
有多关键不用我说了吧!
下面vcIsAllPass这个也不用说了就是容器里的验证是否都通过了!
差不多了剩下的代码就是前端的调用这个地方有些不合理我估计在下一个更新中会处理掉!
<input type=button id= value=统一验证 onclick=<%=vcChecked(A)%> />
客户端的代码生成后在什么地方激活怎么调用要看客户的需求了这个地方需要调用vcChecked(string groupName)来生成js调用代码!
没有这一步的话程序只会为失去焦点做验证提交的时候当然就不会做了!
说了蛮多的不过感觉就是没说清楚!
具体的实现我挑一两个方法贴出来更详细的就下载下面的源码吧!最近一直没时间改这个写了个把月了一起没有把现在有的问题更新!
不哆嗦了看代码:
验证器的其中一个验证项方法:
#region 验证数字(intdouble)
/// <summary>
/// 验证数字(intdouble)
/// </summary>
/// <param name=errorMessage>错误提示字符串</param>
/// <returns></returns>
public Validator<T> Double(string errorMessage)
{
tempJsonString = new StringBuilder();
tempJsonStringAppend(Double:{);
tempJsonStringAppend(ConvertToJsonElement(Msg errorMessage));
tempJsonStringAppend(});
jsonListAdd(tempJsonStringToString());
//判断是否需要忽略验证
if (ignore)
return this;
//验证
if (!IsPattern(validatorRegexs[]))
{
thisReject(errorMessage);
}
return this;
}
#endregion
验证容器的
Code
using System;
using SystemCollectionsGeneric;
using SystemText;
using Validator;
using SystemWeb;
using SystemWebUI;
using SystemWebUIWebControls;
using SystemSecurityPermissions;
namespace Validator
{
[AspNetHostingPermission(SecurityActionDemand Level = AspNetHostingPermissionLevelMinimal)]
public class ValidatorContainer : IObserver
{
/// <summary>
/// 是否开启验证
/// </summary>
private bool isStartCheck;
/// <summary>
/// 验证器所在页对象
/// </summary>
private Page cPage;
/// <summary>
/// 客户端验证器对象名
/// </summary>
private string clientValidatorName;
/// <summary>
/// 验证组名
/// </summary>
private string groupName;
/// <summary>
/// 错误数量
/// </summary>
//private int errorCount;
/// <summary>
/// 前台呈现脚本代码
/// </summary>
private StringBuilder renderString;
/// <summary>
/// 验证器列表
/// </summary>
private List<ISubject> validatorList;
/// <summary>
/// 是否开启验证
/// </summary>
public bool IsStartCheck
{
get { return isStartCheck; }
set { isStartCheck = value; }
}
/// <summary>
/// 客户端验证器对象名
/// </summary>
public string ClientValidatorName
{
get { return clientValidatorName; }
set { clientValidatorName = value; }
}
/// <summary>
/// 验证组名
/// </summary>
public string GroupName
{
get { return groupName; }
set { groupName = value; }
}
#region 是否通过验证
/// <summary>
/// 是否通过验证
/// </summary>
public bool IsAllPass
{
get { return GetPassStat(); }
}
/// <summary>
/// 获取验证状态
/// </summary>
/// <returns></returns>
private bool GetPassStat()
{
foreach (ISubject o in validatorList)
{
if (!oIsPass)
return false;
}
return true;
}
#endregion
/// <summary>
/// 是否开启验证
/// </summary>
/// <param name=_isStartCheck></param>
public ValidatorContainer(bool _isStartCheck)
{
validatorList = new List<ISubject>();
clientValidatorName = Validator;
thisisStartCheck = _isStartCheck;
}
/// <summary>
/// 验证器列表
/// </summary>
public List<ISubject> ValidatorList
{
get { return validatorList; }
set { validatorList = value; }
}
/// <summary>
/// 添加字符串验证器
/// </summary>
/// <param name=_validator>字符串验证器对象</param>
public ValidatorValidator<string> Add(ValidatorValidator<string> _validator)
{
if (thisgroupName != _All)
if (thisgroupName != _validatorGroupName)
_validatorIgnore = true;
validatorListAdd(_validator);
return _validator;
}
/// <summary>
/// 添加整数验证器
/// </summary>
/// <param name=_validator>整数验证器对象</param>
public ValidatorValidator<int> Add(ValidatorValidator<int> _validator)
{
if (thisgroupName != _All)
if (thisgroupName != _validatorGroupName)
_validatorIgnore = true;
validatorListAdd(_validator);
return _validator;
}
/// <summary>
/// 获取所有分组的验证不通过数量
/// </summary>
/// <returns></returns>
public int GetErrorCount()
{
return ;
}
/// <summary>
/// 获取某分组验证不能过数目
/// </summary>
/// <param name=_groupName></param>
/// <returns></returns>
public int GetErrorCount(string _groupName)
{
return ;
}
#region 实现观察者接口
public void GetValidatorStat(ISubject subject)
{
//根据subject中的信息记录到汇总信息中
//subjectGroupName;
//subjectIgnore;
//subjectIsPass;
//subjectErrorMessage;
}
#endregion
/// <summary>
/// 生成客户端验证json代码
/// </summary>
/// <returns></returns>
public void RenderClinetValidator()
{
RenderClinetValidator(null);
}
/// <summary>
/// 是否存在页面注册区域
/// </summary>
/// <returns>存在的区域枚举</returns>
private FormOrHeader GetRegisterRegion()
{
cPage = (Page)HttpContextCurrentHandler;
if (cPageHeader != null)
{
return FormOrHeaderHeader;
}
else if (cPageForm != null)
{
return FormOrHeaderForm;
}
return FormOrHeaderNull;
}
/// <summary>
/// 生成客户端验证json代码
/// </summary>
/// <param name=_clientValidatorName>客户端验证器名称</param>
/// <returns></returns>
public void RenderClinetValidator(string _clientValidatorName)
{
FormOrHeader formOrHeader = GetRegisterRegion();
if (formOrHeader == FormOrHeaderNull)
throw new Exception(对不起页面不存在必须存在标记为\runat=server\的Form或是Header标记!请确认!);
//设置客户端验证对象名
if (!stringIsNullOrEmpty(_clientValidatorName))
thisclientValidatorName = _clientValidatorName;
//按组名排序
validatorListSort(new GroupComparer());
renderString = new StringBuilder();
renderStringAppendLine(<script type=text/javascript>);
renderStringAppendLine(var + clientValidatorName + ;);
renderStringAppendLine(function InitValidator(){);
renderStringAppendLine(clientValidatorName + =new MyValidator( + clientValidatorName + Container););
renderStringAppendLine(});
renderStringAppendLine(BindEvent(windowonloadInitValidator););
renderStringAppendLine(var + clientValidatorName + Container={);
int i = ;
string currentGroupName;
string preGroupName;
string nextGroupName;
int totalCount = validatorListCount;
foreach (ISubject o in validatorList)
{
//设置组名
currentGroupName = validatorList[i]GroupName;
preGroupName = validatorList[(i ) < ? : (i )]GroupName;
nextGroupName = validatorList[(i + ) > totalCount ? totalCount : (i + )]GroupName;
if (i == )
renderStringAppend(currentGroupName + :{);
if (currentGroupName != preGroupName)
{
renderStringAppend(});
renderStringAppend( + currentGroupName + :{);
}
i++;
renderStringAppend(oJsonString);
if (i != validatorListCount)
if (currentGroupName == nextGroupName)
renderStringAppend(oJsonString == stringEmpty ? : );
}
renderStringAppend(});
renderStringAppendLine(};);
renderStringAppendLine(</script>);
//加载图片
string imageTemplate = <img id=falseIcon src={} style=display:none>;
LiteralControl imageUrlLiterl = new LiteralControl(stringFormat(imageTemplate cPageClientScriptGetWebResourceUrl(thisGetType() ValidatorResourcesiconsgif)));
cPagePageControlsAddAt( imageUrlLiterl);
//装载样式
string includeTemplate = <link rel=stylesheet text=text/css />;
string includeLocation = cPageClientScriptGetWebResourceUrl(thisGetType() ValidatorResourcesvalidatorcss);
LiteralControl includeLocationLiteral = new LiteralControl(StringFormat(includeTemplate includeLocation));
//装载js
string jsTemplate = <script src={} type=text/javascript></script>;
string scriptLocation = cPageClientScriptGetWebResourceUrl(thisGetType() ValidatorResourcesMyValidatorjs);
LiteralControl scriptLocationLiteral = new LiteralControl(StringFormat(jsTemplate scriptLocation));
if (formOrHeader == FormOrHeaderHeader)
{
cPageHeaderControlsAdd(includeLocationLiteral);
cPageHeaderControlsAdd(scriptLocationLiteral);
cPageHeaderControlsAdd(new LiteralControl(renderStringToString()));
}
else
{
cPageControlsAddAt( includeLocationLiteral);
cPageControlsAddAt( scriptLocationLiteral);
cPageClientScriptRegisterClientScriptBlock(cPageGetType() thisclientValidatorName renderStringToString());
}
}
/// <summary>
/// 验证所有组
/// </summary>
/// <returns>组验证调用函数</returns>
public string Checked()
{
return Checked(_All);
}
/// <summary>
/// 验证特定组
/// </summary>
/// <param name=groupName>组名(_All表示验证所有组)</param>
/// <returns>组验证调用函数</returns>
public string Checked(string _groupName)
{
string checkGroup = _All;
if (!stringIsNullOrEmpty(_groupName))
checkGroup = _groupName;
return clientValidatorName + ValidateByGroup( + checkGroup + );;
}
}
/// <summary>
/// Form或Header
/// </summary>
public enum FormOrHeader : byte
{
/// <summary>
/// 头部
/// </summary>
Header
/// <summary>
/// 表单
/// </summary>
Form
/// <summary>
/// 无
/// </summary>
Null
}
}
集成在项目中的js的代码我也不贴了这些js的引用及css的引用程序都包办了
调用肯定只要引用Validatordll就可以了!
代码质量不高想到哪写到哪一直没有时间整理到时整理出来完善版的再发上来希望大家给予批评!
真诚的感谢您抽时间来看我的文章!
谢了!
源码发上来有兴趣的可以下去看看(注:TestWeb是上面这个测试页面的站点另外一个大家自己猜是什么好了!)