您的位置:首页 > 其它

数据验证随想

2014-04-24 19:55 141 查看

前言

  在方法中,为了保证数据的有效性,难免对它做检查,在参数多的时候,if判断就会很多。示例代码如下:

public ActionResult Login(string uid, string pwd)
{
ResultDTO rdto = new ResultDTO();
if (uid == null || uid.Length == 0)
{
rdto.Message = "用户名不能为空";
return this.Json(rdto);
}
if (pwd == null || pwd.Length == 0)
{
rdto.Message = "密码不能为空";
return this.Json(rdto);
}
...
}


  这仅仅是2个字段进行非空验证,如果是字段多,而且验证方式更多,则难免也写N个if判断。

  MVC中提供了验证机制,在实体上添加Attrbute即可指明验证参数的方法。但是很多时候参数是不固定的呀,多变的,比如说查询条件,少则三四个,多则十几个,更多的也有。所以在验证参数这一块,还不知道有什么方法可以简化我们的工作量,如果有朋友知道,还请告知,先谢过。

  来说说楼主的方法,目前仅在猜想中,未应用于任何项目。

初步方案

  先看看楼主的调用方式:

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
namespace MacRead
{
class Program
{
static void Main(string[] args)
{
//参数格式:模仿MVC中的FormValueCollection,或Request["xxx"]
var form = new NameValueCollection();
form["UserName"] = "admin";
form["password"] = "*********";
form["Sex"] = "2";
//form["Sex"] = null;
form["Email"] = "304885433@qq.com";
/*
指明必须验证的字段
验证数据 自动填充Model
*/
var Request = HttpContext.Current.Request;
var v = ValidateHelp.BeginEntity<User>()
.Require("UserName", "password", "Email", "Sex")
.IsNullOrEmpty(form["UserName"], "用户名不能为空")
.IsNullOrEmpty(form["password"], "密码不能为空", "password")
.IsInt(form["Sex"], "无法识别性别", "Sex")
.IsEmail(form["Email"], "邮箱地址错误", "Email");
if (v.IsValid)
{
Console.WriteLine("验证通过");
var m = v.Entity as User;
}
else
{
Console.WriteLine("验证未通过 信息:" + v.ErrorMsg);
}

/*
验证数据 自动填充Dictionary
*/
form["Email"] = "304885433";
v = ValidateHelp.BeginDic()
.Require("UserName", "password", "Email", "Sex")
.IsNullOrEmpty(form["UserName"], "用户名不能为空", "UserName")
.IsNullOrEmpty(form["password"], "密码不能为空", "password")
.IsInt(form["Sex"], "无法识别性别", "Sex")
.IsEmail(form["Email"], "邮箱地址错误", "Email");
if (v.IsValid)
{
Console.WriteLine("验证通过");
}
else
{
Console.WriteLine("验证未通过 信息:" + v.ErrorMsg);
}
foreach (var d in v.Data)
{
Console.WriteLine("{0}={1}", d.Key, d.Value);
}
}
}
public class User
{
public string UserName { get; set; }
public string password { get; set; }
public int Sex { get; set; }
public string Email { get; set; }
}
}


  定义了一个 ValidateHelp 对象,在初始化的时候,可以选择数据容器。

  解释一下,为什么需要数据容器。我们在方法开始对参数做验证,验证通过后必然是要去使用它,使用时难免需要类型转换,所以干脆在验证的时候去指明一个数据容器,在数据验证通过后,将数据填充到指定的容器中即可,验证完毕后,直接取出来使用。

  看看其中一个验证方法的定义。

public ValidateHelp IsNullOrEmpty(string s, string msg = null, string propertyName = null)


  s:被验证的字符串

  msg:验证失败的提示消息

  propertyName:验证成功后保存的属性名称,当数据容器为Dictionary时为Key,当数据容器为Model时为属性名。

  所以楼主的目的就是验证失败时,得到错误信息,验证成功,能自动把类型变换后的值保存。

反射的优化方案

  由于上面说了,目前还在猜想阶段,在对实体进行赋值时,使用的是反射。

  性能要求不高的场景下,用反射即可,要求高的时候,推荐一个朋友写的库,在整个软件生命周期,调用次数达到1w以上,性能十分强劲,大家可以看看,《Literacy 快速反射读写对象属性,字段》

说说Require

  楼主在原来的项目中做查询,条件总数大概有十几个,其中4-5个是必需的,其他为null时可以忽略,加入Require方法,目的则是指明部分属性是不可以为null的。在上面的实例中,对于Sex字段验证时,该值为null,最后依然验证通过。

  源码如下:

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;

namespace MacRead
{
public class RequestValidateHelp
{
#region 属性
//数据容器字典
public Dictionary<string, object> Data { get; private set; }
//数据容器实体
public Object Entity { get; private set; }
//错误信息
public string ErrorMsg { get; private set; }
// true:验证通过 false:验证未通过
public bool IsValid { get; private set; }
#endregion

#region 字段

//数据容器实体类型
private Type EntityType;
//必需的字段信息
private string[] RequiredProperty;
//数据源
private NameValueCollection Container;
#endregion

#region 静态方法 得到对象实例
public static RequestValidateHelp Begin()
{
return new RequestValidateHelp();
}

public static RequestValidateHelp BeginDic()
{
var v = new RequestValidateHelp();
v.Data = new Dictionary<string, object>();
return v;
}

public static RequestValidateHelp BeginEntity<T>()
{
var v = new RequestValidateHelp();
v.EntityType = typeof(T);
v.Entity = Activator.CreateInstance(v.EntityType);
return v;
}
#endregion

#region 辅助方法
public object this[string key]
{
get
{
object o;
if (Data.TryGetValue(key, out o)) return o;
return null;
}
}

/// <summary>
/// 指明必需的属性 不允许为null
/// </summary>
/// <param name="requiredProperty"></param>
/// <returns></returns>
public RequestValidateHelp Require(params string[] requiredProperty)
{
if (this.RequiredProperty != null) throw new Exception("Require can‘t be called multiple times ");
this.RequiredProperty = requiredProperty;
return this;
}
#endregion

#region 私有方法
private RequestValidateHelp()
{
IsValid = true;//默认验证通过
var Request = System.Web.HttpContext.Current.Request;
//构造请求上下文(无论是get还是post都允许携带Url参数,post时允许携带body
Container = new NameValueCollection(Request.QueryString);
if (Request.HttpMethod == "POST")
{
Container.Add(Request.Form);
}
}

/// <summary>
/// 数据验证不通过
/// </summary>
/// <param name="msg">错误信息提示</param>
/// <returns></returns>
private RequestValidateHelp SetInValid(string msg)
{
IsValid = false;
ErrorMsg = msg;
return this;
}

/// <summary>
/// 数据验证通过
/// </summary>
/// <param name="value">数据</param>
/// <param name="keyName">属性名</param>
/// <returns></returns>
private RequestValidateHelp SetValid(object value, string keyName)
{
if (!IsValid) IsValid = true;
if (keyName != null && keyName.Length > 0)
{
if (Data != null)
{
Data[keyName] = value;
}
else if (Entity != null)
{
var p = Entity.GetType().GetProperty(keyName);
//为对象属性赋值
p.SetValue(Entity, value, null);
}
}
return this;
}

/// <summary>
/// 验证检查 返回True 终止验证,false 继续验证
/// </summary>
/// <param name="s">输入值</param>
/// <param name="propertyName">属性值</param>
/// <returns></returns>
private bool Interrupt(string s, string propertyName)
{
//验证无效,直接返回
if (!IsValid) return true;
/*继续验证条件
* s非空
* 未指明属性名
* 未定义必需的属性
*/
if (s != null ||
propertyName == null ||
propertyName.Length == 0 ||
RequiredProperty == null ||
RequiredProperty.Length == 0) return false;
//必需属性数组中包含属性时,
var i = Array.IndexOf(RequiredProperty, propertyName);
if (i > -1) // 属性还被指定必需,则直接判定为无效,终止验证
{
IsValid = false;
ErrorMsg = propertyName + " is null";
}
// 属性为null时 中断验证
return true;
}
#endregion

#region 验证方法
public RequestValidateHelp IsNullOrEmpty(string keyName, string msg = null, string propertyName = null)
{
var s = Container[keyName];
if (Interrupt(s, keyName)) return this;
if (s == null || s.Length == 0)
{
return SetInValid(msg);
}
return SetValid(s, propertyName ?? keyName);
}

public RequestValidateHelp IsInt(string keyName, string msg = null, string propertyName = null)
{
var s = Container[keyName];
if (Interrupt(s, keyName)) return this;
int i;
if (!int.TryParse(s, out i))
{
return SetInValid(msg);
}
return SetValid(s, propertyName ?? keyName);
}

public RequestValidateHelp IsDouble(string keyName, string msg = null, string propertyName = null)
{
var s = Container[keyName];
if (Interrupt(s, keyName)) return this;
double i;
if (!double.TryParse(s, out i))
{
return SetInValid(msg);
}
return SetValid(s, propertyName ?? keyName);
}

public RequestValidateHelp IsLong(string keyName, string msg = null, string propertyName = null)
{
var s = Container[keyName];
if (Interrupt(s, keyName)) return this;
long i;
if (!long.TryParse(s, out i))
{
return SetInValid(msg);
}
return SetValid(s, propertyName ?? keyName);
}

public RequestValidateHelp IsFloat(string keyName, string msg = null, string propertyName = null)
{
var s = Container[keyName];
if (Interrupt(s, keyName)) return this;
float i;
if (!float.TryParse(s, out i))
{
return SetInValid(msg);
}
return SetValid(s, propertyName ?? keyName);
}

public RequestValidateHelp IsBool(string keyName, string msg = null, string propertyName = null)
{
var s = Container[keyName];
if (Interrupt(s, keyName)) return this;
bool i;
if (!bool.TryParse(s, out i))
{
return SetInValid(msg);
}
return SetValid(s, propertyName ?? keyName);
}

public RequestValidateHelp IsDateTime(string keyName, string msg = null, string propertyName = null)
{
var s = Container[keyName];
if (Interrupt(s, keyName)) return this;
DateTime i;
if (!DateTime.TryParse(s, out i))
{
return SetInValid(msg);
}
return SetValid(s, propertyName ?? keyName);
}

public RequestValidateHelp IsEnum<T>(string keyName, string msg = null, string propertyName = null) where T : struct
{
var s = Container[keyName];
T i;
if (!Enum.TryParse(s, true, out i))
{
return SetInValid(msg);
}
return SetValid(s, propertyName ?? keyName);
}

private static Regex regChar = new Regex(@"^[A-Za-z]+$");
public RequestValidateHelp IsChar(string keyName, string msg = null, string propertyName = null)
{
var s = Container[keyName];
if (Interrupt(s, keyName)) return this;
if (!regChar.IsMatch(s))
{
return SetInValid(msg);
}
return SetValid(s, propertyName ?? keyName);
}

private static Regex regNumber = new Regex(@"^\d+$");
public RequestValidateHelp IsNumber(string keyName, string msg = null, string propertyName = null)
{
var s = Container[keyName];
if (Interrupt(s, string.Empty)) return this;
if (!regNumber.IsMatch(s))
{
return SetInValid(msg);
}
return SetValid(s, string.Empty);
}

private static Regex regChinese = new Regex(@"^[\u4e00-\u9fa5]+$");
public RequestValidateHelp IsChinese(string keyName, string msg = null, string propertyName = null)
{
var s = Container[keyName];
if (Interrupt(s, keyName)) return this;
if (!regChinese.IsMatch(s))
{
return SetInValid(msg);
}
return SetValid(s, propertyName ?? keyName);
}

private static Regex regEmail = new Regex(@"^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$");
public RequestValidateHelp IsEmail(string keyName, string msg = null, string propertyName = null)
{
var s = Container[keyName];
if (Interrupt(s, keyName)) return this;
if (!regEmail.IsMatch(s))
{
return SetInValid(msg);
}
return SetValid(s, propertyName ?? keyName);
}

private static Regex regIP = new Regex(@"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$");
public RequestValidateHelp IsIP(string keyName, string msg = null, string propertyName = null)
{
var s = Container[keyName];
if (Interrupt(s, keyName)) return this;
if (!regIP.IsMatch(s))
{
return SetInValid(msg);
}
return SetValid(s, propertyName ?? keyName);
}

private static Regex regUrl = new Regex(@"^([a-zA-z]+://)?(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\S*)?$");
public RequestValidateHelp IsUrl(string keyName, string msg = null, string propertyName = null)
{
var s = Container[keyName];
if (Interrupt(s, keyName)) return this;
if (!regUrl.IsMatch(s))
{
return SetInValid(msg);
}
return SetValid(s, propertyName ?? keyName);
}

public RequestValidateHelp IsRegex(string keyName, string pattern, string msg = null, string propertyName = null)
{
var s = Container[keyName];
if (Interrupt(s, string.Empty)) return this;
if (!Regex.IsMatch(s, pattern))
{
return SetInValid(msg);
}
return SetValid(s, string.Empty);
}
#endregion
}
}


RequestValidateHelp.cs
  在验证方法的参数中,依旧保留了2个Key一样的参数,看定义:

public RequestValidateHelp IsNullOrEmpty(string keyName, string msg = null, string propertyName = null)


  propertyName 作为在保存验证成功后的值的键名或者属性名,如果为null,则默认使用keyName,即取值的Key。

  为什么需要这样多次一举,看示例吧。

$("#btnLogin").on('click',function(){
var viewModel = {
uid: $("#txtUserName").val(),
pwd: $("#txtPassword").val()
};
if (viewModel.uid == '') {
alert('用户名不能为空');
return;
}
if (viewModel.pwd == '') {
alert('密码不能为空');
return;
}
$.post('/Account/Login?tt=1', viewModel, function (response) {
if (!response.Result) {
alert(response.Message);
return;
}
window.location.href = 'admin/index.html';
},'json');
});


  js提交到后端的json数据是uid和pwd,但实际上后台定义的实体类似 UserName,Password等属性,呃,可能是不想过多的让别人了解后台的类定义吧。。。

写在最后

  由于是近2天的思考,楼主希望能同大家交流下,时间有点仓促,代码中难免有疏漏或者错误的地方。

  重在思想,代码的表现形式会有很多,更优秀的代码就是下一行。

  下班了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: