MVC查询数据接收及校验

时间:2023-03-09 01:38:06
MVC查询数据接收及校验

本来想写一篇aspx的TreeView控件绑值的文章的,在写案例的时候,写了一半,发现有些地方还得考虑以下,就留待下次了。

这一篇的话,是最近在开发一个项目的时候,有大量的页面和数据表,需要花式查询,

后台接收前台传递过来的数据的时候,被虐的欲仙欲死,大量的校验和重复代码,

后来找到了一种非常不错的方法,分享出来,下面是正文。。。。。

使用过MVC的人都知道,它有一个非常方便的功能,就是自动绑值,先来一个最简单的:

         public ActionResult Index(string userName, int type) {
/*
代码块
*/
return View();
}

当从前台传递过来的数据中,有两个参数名字为userName或type时,MVC会自动帮我们将参数类型转好,值给好。

我们要做的无非是直接使用罢了,但是,当要传递的值非常多的时候,无论是写还是看,都会非常吃力,比如这样:

         public ActionResult Index01(string userName, int type, string code, int height, string sex, DateTime startTime, DateTime endTime) {
/*
代码块
*/
return View();
}

其实,七个查询参数并不多,当参数数量达到十个,二十个时,相信我,你会炸的,

一般这个时候,我们可用选择使用对象,就像这样:

         public ActionResult Index01(User user) {
/*
代码块
*/
return View();
} /// <summary>
/// 用户类
/// </summary>
public class User { /// <summary>
/// 用户姓名
/// </summary>
public string userName; /// <summary>
/// 用户类型
/// </summary>
public int type; /// <summary>
/// 身份证号
/// </summary>
public string code; /// <summary>
/// 用户身高
/// </summary>
public int height; /// <summary>
/// 用户性别
/// </summary>
public string sex; #region 注册时间范围 public DateTime startTime;
public DateTime endTime; #endregion }

这样写是不是看起来舒服了很多?MVC同样能够帮你将数据依次绑好,同时,代码的复用率也会提高很多,

现在解决了数据接收的问题,接下来的,就是数据的校验了。

==========分隔符==========

拿到参数后,我们是不能立刻去使用的,需要对参数进行一次校验,校验的目的,就是防止有人恶意传递错误数据给后台,

若不进行数据校验的话,很容易导致项目崩溃,或者数据丢失等等一系列问题,简单的说一些校验类型吧,

string 变量,主要校验是否包含sql注入字符,然后判断是否为null,还要去掉多余的空格

int 变量,主要检验是否在某一个范围内,以及默认值是0还是-1,或者是其它的一些数字

DateTime 变量,一般都是两个一起使用,一个开始一个结束,这个时候我们就要校验,开始时间是否小于或等于结束时间,以及结束时间是否小于或等于当前时间,

还有一点值得注意的是,若开始时间和结束时间精确到天时,若是同一天,在数据库是无法查出数据的,所以必须精确到秒

我们按这个思路去添加校验:

        public ActionResult Index01(User user) {

            // 字符串校验
// 判断是否为空为null
if (string.IsNullOrEmpty(user.userName))
user.userName = user.userName.Trim();// 去掉多余空格
// SQL注入校验
if (CheckSQL(user.userName))
user.userName = "";// 替换掉带有SQL注入的字符 // 数字校验
if (user.type < || < user.type)
user.type = ;// 当范围不在[0,20]时,给默认值0 // 时间校验
/*
没有六七十行搞不定,就不写了,,,
*/ return View();
}

看起来还是不错的,但如果考虑到,每个数据的范围不一样,校验也是各不相同,而且,一个数据校验最少就得写两行代码,

当参数多了的时候,光校验代码都得写上一两百行,可以想想,如果有一百个类似的页面,呵呵。。。

不仅仅是看的难受,维护也是相当困难的,

所以我就想,能不能前台向后台请求的数据,都用一个类来接收,所有的校验都写在这个类里面,

类的每个参数,在输出的时候,都进行校验,这样可用极大的省略,接收数据之后在视图中的校验,而是将校验放在一起,

同时,相同校验方法的的参数,可用限制参数名为同一个,代码的复用率也会得到提升,对于维护和修改也能轻松进行,,,

说干就干,当时写出来的类,经过这么久的修改和添加,已经可以拿出来溜溜了,先上使用代码吧:

         public ActionResult Index01(ReqData data) {

             string sqlstr = string.Format(" select * from dt_user where userName='{0}' ", data.UserName);

             /*
执行sql语句,以及其他操作,,,
*/ return View();
}

有没有瞬间感觉画风不对,说好的校验哪去了?怎么能直接使用??

其实,所有的校验都在ReqData这个类里面,可以在它里面添加自己需要的参数,以及对应的校验方法,这样,使用的时候就会非常简单了

我主要想要分享的是一种封装的思想和技巧,可能ReqData这个类还是很简陋,但还是希望能对大家有所帮助,好像有点长,贴上代码先:

 using System;

 namespace Demo.Model {

     /// <summary>
/// 用于接收前台传过来的数据
/// </summary>
public struct ReqData { #region 分页数据 /// <summary>
/// 数据总行数
/// </summary>
public int PageCount { get; set; } /// <summary>
/// 当前页码
/// </summary>
public int PageIndex {
get {
if (pageIndex == )
pageIndex = ;
return pageIndex;
}
set {
pageIndex = value;
}
}
private int pageIndex; /// <summary>
/// 每页行数
/// </summary>
public int PageSize {
get {
if (pageSize == )
pageSize = ;
return pageSize;
}
set {
pageSize = value;
}
}
private int pageSize; /// <summary>
/// 页面跳转链接,带参数
/// 用于分页跳转
/// </summary>
public string PageUrl { get; set; } /// <summary>
/// 页面跳转链接,不带参数
/// 用于删除时跳转
/// </summary>
public string GetPageUrl {
get {
// 判断是否为空
if (PageUrl == null)
return ""; // 检测是否有参数
int index = PageUrl.LastIndexOf("?");
// 去掉参数
if (index > )
return PageUrl.Substring(, index);
return PageUrl;
}
} #endregion #region 页面参数 /// <summary>
/// 视图样式,{ txt:列表视图,img:图片视图 }
/// </summary>
public string Show {
get {
CheckStr(ref show); if (string.IsNullOrEmpty(show))
show = "txt";
if (show != "txt" && show != "img")
show = "txt";
return show;
}
set { show = value; }
}
private string show; /// <summary>
/// 导航栏标题
/// </summary>
public string Title {
get { return CheckStr(ref title); }
set { title = value; }
}
private string title; #endregion #region 查询参数 /// <summary>
/// 用户编号
/// </summary>
public int? ID {
get { return id; }
set { id = value; }
}
private int? id; /// <summary>
/// 用户名
/// </summary>
public string UserName {
get { return CheckStr(ref userName); }
set { userName = value; }
}
private string userName; /// <summary>
/// 用户等级,范围:[0,3]
/// </summary>
public int? Leavel {
get { return GetNumInMinToMax(ref leavel, , ); }
set { leavel = value; }
}
private int? leavel; #region 时间参数 #region 时间接收 private DateTime? start_Time;
/// <summary>
/// 开始时间,时分秒为 0:0:0,并且不能大于End_Time
/// </summary>
public DateTime? Start_Time {
get {
// 允许开始时间为空
if (start_Time == null)
return start_Time; // 若开始时间大于当前时间
if (start_Time.Value > DateTime.Now)
// 开始时间为当前时间
start_Time = DateTime.Now; // 当结束时间不为空
if (end_Time != null)
// 当开始时间大于结束时间时
if (start_Time > End_Time)
// 取结束时间当天的凌晨
start_Time = new DateTime(End_Time.Value.Year, End_Time.Value.Month, End_Time.Value.Day, , , ); return start_Time;
}
set { start_Time = value; }
} private DateTime? end_Time;
/// <summary>
/// 结束时间,时分秒为 23:59:59,并且不能大于当前时间
/// </summary>
public DateTime? End_Time {
get {
// 允许结束时间为空
if (end_Time == null)
return end_Time; // 若结束时间大于当前时间
if (end_Time.Value >= DateTime.Now)
// 结束时间为当前时间
end_Time = DateTime.Now;
else {
// 获取结束时间的信息
int year = end_Time.Value.Year;
int month = end_Time.Value.Month;
int day = end_Time.Value.Day; int hour = end_Time.Value.Hour;
int minute = end_Time.Value.Minute;
int second = end_Time.Value.Second; // 当时分秒均为0时,为结束时间加上时分秒
if (hour == && minute == && second == ) {
DateTime now = DateTime.Now;
// 若结束时间的年月日正好是当天
if (now.Year == year && now.Month == month && now.Day == day)
end_Time = now;
// 否则,给到结束时间那天,最后一秒
else
end_Time = new DateTime(year, month, day, , , );
}
} return end_Time;
}
set { end_Time = value; }
} #endregion #region 时间输出 /// <summary>
/// 时间字符串返回格式
/// 若不设置,默认为"yyyy-MM-dd HH:mm:ss"
/// </summary>
public string Format {
get {
if (format == null)
format = "yyyy-MM-dd HH:mm:ss";
return format;
}
set { format = value; }
}
private string format; /// <summary>
/// 用于返回开始时间字符串
/// </summary>
public string GetStarTimeStr {
get {
if (Start_Time.HasValue)
return Start_Time.Value.ToString(Format);
return "";
}
} /// <summary>
/// 用于返回结束时间字符串
/// </summary>
public string GetEndTimeStr {
get {
if (End_Time.HasValue)
return End_Time.Value.ToString(Format);
return "";
}
} #endregion #endregion #endregion #region 校验方法 /// <summary>
/// 保证num的值范围为,[min,max]
/// </summary>
/// <param name="num">原始数据</param>
/// <param name="min">最小值</param>
/// <param name="max">最大值</param>
/// <param name="def">默认值(不填时,默认值为最小值)</param>
/// <returns></returns>
public int? GetNumInMinToMax(ref int? num, int min, int max, int? def = null) {
// 若def没有值,将最小值给它
if (!def.HasValue)
def = min;
// 若num没有值,将默认值给它
if (!num.HasValue)
num = def;
// 若num的值小于最小值,或大于最大值,将默认值给它
else if (num < min || max < num)
num = def; return num;
} /// <summary>
/// 将字符串去掉空格,并进行敏感字符检测
/// </summary>
/// <param name="str">原字符串</param>
/// <param name="Ischeck">是否开启敏感字符校验</param>
/// <param name="def">默认的值,字符串为空时,赋此值</param>
/// <returns></returns>
public string CheckStr(ref string str, bool Ischeck = true,string def="") {
if (string.IsNullOrEmpty(str))
return str = def;
str = str.Trim();
if (Ischeck)
if (!GetIsFormText(str))
str = "请不要输入敏感字符!";
return str;
} /// <summary>
/// 检测是否含有危险字符(防止Sql注入)
/// 转自:http://blog.****.net/chaozi/article/details/4462312
/// </summary>
/// <param name="contents">预检测的内容</param>
/// <returns>返回True或false</returns>
public static bool GetIsFormText(string contents) {
bool bReturnValue = false;
if (contents.Length > ) {
//convert to lower
string sLowerStr = contents.ToLower();
//RegularExpressions
string sRxStr = "(/sand/s)|(/sand/s)|(/slike/s)|(select/s)|(insert/s)|" +
"(delete/s)|(update/s[/s/S].*/sset)|(create/s)|(/stable)|(<[iframe|/iframe|" +
"script|/script])|(')|(/sexec)|(/sdeclare)|(/struncate)|(/smaster)|(/sbackup)|(/smid)|(/scount)";
//Match
bool bIsMatch = false;
System.Text.RegularExpressions.Regex sRx = new System.Text.RegularExpressions.Regex(sRxStr);
bIsMatch = sRx.IsMatch(sLowerStr, );
if (bIsMatch) {
bReturnValue = true;
}
}
return bReturnValue;
} #endregion #region 数据绑定方法 /// <summary>
/// 返回指定区间的日期,默认今天
/// </summary>
/// <param name="dateSection"></param>
public void GetDateSection(DateSection dateSection = DateSection.Today) { // 判断枚举中,是否存在此项
if (!Enum.IsDefined(typeof(DateSection), (int)dateSection))
dateSection = DateSection.Today; // 日期
DateTime Date = DateTime.Now; // 倒退的天数
int BackDay = ; switch (dateSection) { #region =====今天===== case DateSection.Today:
End_Time = Date;
Start_Time = new DateTime(Date.Year, Date.Month, Date.Day, , , , );
break; #endregion #region =====昨天===== case DateSection.Yesterday:
Date = DateTime.Now.AddDays(-); End_Time = new DateTime(Date.Year, Date.Month, Date.Day, , , , );
Start_Time = new DateTime(Date.Year, Date.Month, Date.Day, , , , , );
break; #endregion #region =====本周===== case DateSection.ThisWeek:
End_Time = Date; // 获取今天是本周第几天
BackDay = Convert.ToInt32(Date.DayOfWeek.ToString("d")); Date = DateTime.Now.AddDays(-BackDay);
Start_Time = new DateTime(Date.Year, Date.Month, Date.Day, , , , );
break; #endregion #region =====上周===== case DateSection.LastWeek:
BackDay = Convert.ToInt32(Date.DayOfWeek.ToString("d")) + ; // 到上周最后一天
Date = DateTime.Now.AddDays(-BackDay);
End_Time = new DateTime(Date.Year, Date.Month, Date.Day, , , , ); // 到上周第一天
Date = Date.AddDays(-);
Start_Time = new DateTime(Date.Year, Date.Month, Date.Day, , , , ); break; #endregion #region =====本月===== case DateSection.ThisMonth:
End_Time = Date;
Start_Time = new DateTime(Date.Year, Date.Month, , , , , );
break; #endregion #region =====上月===== case DateSection.LastMonth: BackDay = Date.Day; // 到上月最后一天
Date = DateTime.Now.AddDays(-BackDay);
End_Time = new DateTime(Date.Year, Date.Month, Date.Day, , , , ); Start_Time = new DateTime(Date.Year, Date.Month, , , , , );
break; #endregion #region =====今年===== case DateSection.ThisYear:
End_Time = Date;
Start_Time = new DateTime(Date.Year, , , , , , );
break; #endregion #region =====去年===== case DateSection.LastYear:
BackDay = Date.DayOfYear; // 到去年最后一天
Date = DateTime.Now.AddDays(-BackDay);
End_Time = new DateTime(Date.Year, Date.Month, Date.Day, , , , ); Start_Time = new DateTime(Date.Year, , , , , , );
break; #endregion default: break;
} } /// <summary>
/// 保证开始和结束时间绝对不为空
/// </summary>
/// <param name="DateLong">间隔长度,默认:7</param>
/// <param name="dateFormat">间隔单位,默认:Day(天)</param>
public void GetDateNow(int DateLong = , DateFormat dateFormat = DateFormat.Day) { // 校验是否存在此枚举
if (Enum.IsDefined(typeof(DateFormat), (int)dateFormat))
dateFormat = DateFormat.Day; // 初始化结束时间
if (End_Time == null)
End_Time = DateTime.Now; DateTime? star; // 有校验的时间
star = new DateTime();
star = Start_Time;
ChangStar(ref star, End_Time, DateLong, dateFormat);
Start_Time = star; } /// <summary>
/// 根据结束时间,修改开始时间
/// 若开始时间有值,则不改动
/// </summary>
/// <param name="Start">开始时间</param>
/// <param name="End">结束时间</param>
/// <param name="DateLong">间隔长度</param>
/// <param name="dateFormat">间隔单位</param>
private void ChangStar(ref DateTime? Start, DateTime? End, int DateLong, DateFormat dateFormat) { if (Start.HasValue)
return; DateLong = - DateLong; // 获取开始时间
switch (dateFormat) {
// 年份
case DateFormat.Year:
Start = End.Value.AddYears(DateLong);
break;
// 月份
case DateFormat.Month:
Start = End.Value.AddMonths(DateLong);
break;
// 天数
case DateFormat.Day:
Start = End.Value.AddDays(DateLong);
break;
// 小时
case DateFormat.Hour:
Start = End.Value.AddHours(DateLong);
break;
// 分钟
case DateFormat.Minute:
Start = End.Value.AddMinutes(DateLong);
break;
// 秒钟
case DateFormat.Second:
Start = End.Value.AddSeconds(DateLong);
break;
} } #endregion
} /// <summary>
/// 时间格式
/// </summary>
public enum DateFormat {
/// <summary>
/// 年份
/// </summary>
Year,
/// <summary>
/// 月份
/// </summary>
Month,
/// <summary>
/// 天数
/// </summary>
Day,
/// <summary>
/// 小时
/// </summary>
Hour,
/// <summary>
/// 分钟
/// </summary>
Minute,
/// <summary>
/// 秒钟
/// </summary>
Second
} /// <summary>
/// 时间区间
/// </summary>
public enum DateSection {
/// <summary>
/// 今天
/// </summary>
Today,
/// <summary>
/// 昨天
/// </summary>
Yesterday,
/// <summary>
/// 本周,星期天为第一天
/// </summary>
ThisWeek,
/// <summary>
/// 上周,星期天为第一天
/// </summary>
LastWeek,
/// <summary>
/// 本月
/// </summary>
ThisMonth,
/// <summary>
/// 上月
/// </summary>
LastMonth,
/// <summary>
/// 今年
/// </summary>
ThisYear,
/// <summary>
/// 去年
/// </summary>
LastYear
} }

个人觉着,虽然代码一般般,但里面有不少小技巧还是很不错的,适合那些比我还新的新手学习一下,比如时间校验,,那个真的是伤透了心

如果大家发现了有什么bug,欢迎指出,或者有比较有趣的点子也欢迎互相交流,,,嗯,就酱紫,我去纠结TreeView控件绑值的问题,,