本来想写一篇aspx的TreeView控件绑值的文章的,在写案例的时候,写了一半,发现有些地方还得考虑以下,就留待下次了。
这一篇的话,是最近在开发一个项目的时候,有大量的页面和数据表,需要花式查询,
后台接收前台传递过来的数据的时候,被虐的欲仙欲死,大量的校验和重复代码,
后来找到了一种非常不错的方法,分享出来,下面是正文。。。。。
使用过MVC的人都知道,它有一个非常方便的功能,就是自动绑值,先来一个最简单的:
1 public ActionResult Index(string userName, int type) {2 /*3 代码块4 */5 return View();6 }
当从前台传递过来的数据中,有两个参数名字为userName或type时,MVC会自动帮我们将参数类型转好,值给好。
我们要做的无非是直接使用罢了,但是,当要传递的值非常多的时候,无论是写还是看,都会非常吃力,比如这样:
1 public ActionResult Index01(string userName, int type, string code, int height, string sex, DateTime startTime, DateTime endTime) {2 /*3 代码块4 */5 return View();6 }
其实,七个查询参数并不多,当参数数量达到十个,二十个时,相信我,你会炸的,
一般这个时候,我们可用选择使用对象,就像这样:
1 public ActionResult Index01(User user) { 2 /* 3 代码块 4 */ 5 return View(); 6 } 7 8 ///9 /// 用户类10 /// 11 public class User {12 13 ///14 /// 用户姓名15 /// 16 public string userName;17 18 ///19 /// 用户类型20 /// 21 public int type;22 23 ///24 /// 身份证号25 /// 26 public string code;27 28 ///29 /// 用户身高30 /// 31 public int height;32 33 ///34 /// 用户性别35 /// 36 public string sex;37 38 #region 注册时间范围39 40 public DateTime startTime;41 public DateTime endTime;42 43 #endregion44 45 }
这样写是不是看起来舒服了很多?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 < 0 || 20 < user.type) user.type = 0;// 当范围不在[0,20]时,给默认值0 // 时间校验 /* 没有六七十行搞不定,就不写了,,, */ return View(); }
看起来还是不错的,但如果考虑到,每个数据的范围不一样,校验也是各不相同,而且,一个数据校验最少就得写两行代码,
当参数多了的时候,光校验代码都得写上一两百行,可以想想,如果有一百个类似的页面,呵呵。。。
不仅仅是看的难受,维护也是相当困难的,
所以我就想,能不能前台向后台请求的数据,都用一个类来接收,所有的校验都写在这个类里面,
类的每个参数,在输出的时候,都进行校验,这样可用极大的省略,接收数据之后在视图中的校验,而是将校验放在一起,
同时,相同校验方法的的参数,可用限制参数名为同一个,代码的复用率也会得到提升,对于维护和修改也能轻松进行,,,
说干就干,当时写出来的类,经过这么久的修改和添加,已经可以拿出来溜溜了,先上使用代码吧:
1 public ActionResult Index01(ReqData data) { 2 3 string sqlstr = string.Format(" select * from dt_user where userName='{0}' ", data.UserName); 4 5 /* 6 执行sql语句,以及其他操作,,, 7 */ 8 9 return View();10 }
有没有瞬间感觉画风不对,说好的校验哪去了?怎么能直接使用??
其实,所有的校验都在ReqData这个类里面,可以在它里面添加自己需要的参数,以及对应的校验方法,这样,使用的时候就会非常简单了
我主要想要分享的是一种封装的思想和技巧,可能ReqData这个类还是很简陋,但还是希望能对大家有所帮助,好像有点长,贴上代码先:
1 using System; 2 3 namespace Demo.Model { 4 5 ///6 /// 用于接收前台传过来的数据 7 /// 8 public struct ReqData { 9 10 #region 分页数据 11 12 ///13 /// 数据总行数 14 /// 15 public int PageCount { get; set; } 16 17 ///18 /// 当前页码 19 /// 20 public int PageIndex { 21 get { 22 if (pageIndex == 0) 23 pageIndex = 1; 24 return pageIndex; 25 } 26 set { 27 pageIndex = value; 28 } 29 } 30 private int pageIndex; 31 32 ///33 /// 每页行数 34 /// 35 public int PageSize { 36 get { 37 if (pageSize == 0) 38 pageSize = 10; 39 return pageSize; 40 } 41 set { 42 pageSize = value; 43 } 44 } 45 private int pageSize; 46 47 ///48 /// 页面跳转链接,带参数 49 /// 用于分页跳转 50 /// 51 public string PageUrl { get; set; } 52 53 ///54 /// 页面跳转链接,不带参数 55 /// 用于删除时跳转 56 /// 57 public string GetPageUrl { 58 get { 59 // 判断是否为空 60 if (PageUrl == null) 61 return ""; 62 63 // 检测是否有参数 64 int index = PageUrl.LastIndexOf("?"); 65 // 去掉参数 66 if (index > 0) 67 return PageUrl.Substring(0, index); 68 return PageUrl; 69 } 70 } 71 72 #endregion 73 74 #region 页面参数 75 76 ///77 /// 视图样式,{ txt:列表视图,img:图片视图 } 78 /// 79 public string Show { 80 get { 81 CheckStr(ref show); 82 83 if (string.IsNullOrEmpty(show)) 84 show = "txt"; 85 if (show != "txt" && show != "img") 86 show = "txt"; 87 return show; 88 } 89 set { show = value; } 90 } 91 private string show; 92 93 ///94 /// 导航栏标题 95 /// 96 public string Title { 97 get { return CheckStr(ref title); } 98 set { title = value; } 99 }100 private string title;101 102 #endregion103 104 #region 查询参数105 106 ///107 /// 用户编号108 /// 109 public int? ID {110 get { return id; }111 set { id = value; }112 }113 private int? id;114 115 ///116 /// 用户名117 /// 118 public string UserName {119 get { return CheckStr(ref userName); }120 set { userName = value; }121 }122 private string userName;123 124 ///125 /// 用户等级,范围:[0,3]126 /// 127 public int? Leavel {128 get { return GetNumInMinToMax(ref leavel, 0, 3); }129 set { leavel = value; }130 }131 private int? leavel;132 133 #region 时间参数134 135 #region 时间接收136 137 private DateTime? start_Time;138 ///139 /// 开始时间,时分秒为 0:0:0,并且不能大于End_Time140 /// 141 public DateTime? Start_Time {142 get {143 // 允许开始时间为空144 if (start_Time == null)145 return start_Time;146 147 // 若开始时间大于当前时间148 if (start_Time.Value > DateTime.Now)149 // 开始时间为当前时间150 start_Time = DateTime.Now;151 152 // 当结束时间不为空153 if (end_Time != null)154 // 当开始时间大于结束时间时155 if (start_Time > End_Time)156 // 取结束时间当天的凌晨157 start_Time = new DateTime(End_Time.Value.Year, End_Time.Value.Month, End_Time.Value.Day, 0, 0, 0);158 159 return start_Time;160 }161 set { start_Time = value; }162 }163 164 private DateTime? end_Time;165 ///166 /// 结束时间,时分秒为 23:59:59,并且不能大于当前时间167 /// 168 public DateTime? End_Time {169 get {170 // 允许结束时间为空171 if (end_Time == null)172 return end_Time;173 174 // 若结束时间大于当前时间175 if (end_Time.Value >= DateTime.Now)176 // 结束时间为当前时间177 end_Time = DateTime.Now;178 else {179 // 获取结束时间的信息180 int year = end_Time.Value.Year;181 int month = end_Time.Value.Month;182 int day = end_Time.Value.Day;183 184 int hour = end_Time.Value.Hour;185 int minute = end_Time.Value.Minute;186 int second = end_Time.Value.Second;187 188 // 当时分秒均为0时,为结束时间加上时分秒189 if (hour == 0 && minute == 0 && second == 0) {190 DateTime now = DateTime.Now;191 // 若结束时间的年月日正好是当天192 if (now.Year == year && now.Month == month && now.Day == day)193 end_Time = now;194 // 否则,给到结束时间那天,最后一秒195 else196 end_Time = new DateTime(year, month, day, 23, 59, 59);197 }198 }199 200 return end_Time;201 }202 set { end_Time = value; }203 }204 205 #endregion206 207 #region 时间输出208 209 ///210 /// 时间字符串返回格式211 /// 若不设置,默认为"yyyy-MM-dd HH:mm:ss"212 /// 213 public string Format {214 get {215 if (format == null)216 format = "yyyy-MM-dd HH:mm:ss";217 return format;218 }219 set { format = value; }220 }221 private string format;222 223 ///224 /// 用于返回开始时间字符串225 /// 226 public string GetStarTimeStr {227 get {228 if (Start_Time.HasValue)229 return Start_Time.Value.ToString(Format);230 return "";231 }232 }233 234 ///235 /// 用于返回结束时间字符串236 /// 237 public string GetEndTimeStr {238 get {239 if (End_Time.HasValue)240 return End_Time.Value.ToString(Format);241 return "";242 }243 }244 245 #endregion246 247 #endregion248 249 #endregion250 251 #region 校验方法252 253 ///254 /// 保证num的值范围为,[min,max]255 /// 256 /// 原始数据257 /// 最小值258 /// 最大值259 /// 默认值(不填时,默认值为最小值)260 ///261 public int? GetNumInMinToMax(ref int? num, int min, int max, int? def = null) {262 // 若def没有值,将最小值给它263 if (!def.HasValue)264 def = min;265 // 若num没有值,将默认值给它266 if (!num.HasValue)267 num = def;268 // 若num的值小于最小值,或大于最大值,将默认值给它269 else if (num < min || max < num)270 num = def;271 272 return num;273 }274 275 /// 276 /// 将字符串去掉空格,并进行敏感字符检测277 /// 278 /// 原字符串279 /// 是否开启敏感字符校验280 /// 默认的值,字符串为空时,赋此值281 ///282 public string CheckStr(ref string str, bool Ischeck = true,string def="") {283 if (string.IsNullOrEmpty(str))284 return str = def;285 str = str.Trim();286 if (Ischeck)287 if (!GetIsFormText(str))288 str = "请不要输入敏感字符!";289 return str;290 }291 292 /// 293 /// 检测是否含有危险字符(防止Sql注入)294 /// 转自:http://blog.csdn.net/chaozi/article/details/4462312295 /// 296 /// 预检测的内容297 ///返回True或false 298 public static bool GetIsFormText(string contents) {299 bool bReturnValue = false;300 if (contents.Length > 0) {301 //convert to lower302 string sLowerStr = contents.ToLower();303 //RegularExpressions304 string sRxStr = "(/sand/s)|(/sand/s)|(/slike/s)|(select/s)|(insert/s)|" +305 "(delete/s)|(update/s[/s/S].*/sset)|(create/s)|(/stable)|(<[iframe|/iframe|" +306 "script|/script])|(')|(/sexec)|(/sdeclare)|(/struncate)|(/smaster)|(/sbackup)|(/smid)|(/scount)";307 //Match308 bool bIsMatch = false;309 System.Text.RegularExpressions.Regex sRx = new310 311 System.Text.RegularExpressions.Regex(sRxStr);312 bIsMatch = sRx.IsMatch(sLowerStr, 0);313 if (bIsMatch) {314 bReturnValue = true;315 }316 }317 return bReturnValue;318 }319 320 #endregion321 322 #region 数据绑定方法323 324 ///325 /// 返回指定区间的日期,默认今天326 /// 327 /// 328 public void GetDateSection(DateSection dateSection = DateSection.Today) {329 330 // 判断枚举中,是否存在此项331 if (!Enum.IsDefined(typeof(DateSection), (int)dateSection))332 dateSection = DateSection.Today;333 334 // 日期335 DateTime Date = DateTime.Now;336 337 // 倒退的天数338 int BackDay = 0;339 340 switch (dateSection) {341 342 #region =====今天=====343 344 case DateSection.Today:345 End_Time = Date;346 Start_Time = new DateTime(Date.Year, Date.Month, Date.Day, 0, 0, 0, 0);347 break;348 349 #endregion350 351 #region =====昨天=====352 353 case DateSection.Yesterday:354 Date = DateTime.Now.AddDays(-1);355 356 End_Time = new DateTime(Date.Year, Date.Month, Date.Day, 23, 59, 59, 999);357 Start_Time = new DateTime(Date.Year, Date.Month, Date.Day, 0, 0, 0, 0, 0);358 break;359 360 #endregion361 362 #region =====本周=====363 364 case DateSection.ThisWeek:365 End_Time = Date;366 367 // 获取今天是本周第几天368 BackDay = Convert.ToInt32(Date.DayOfWeek.ToString("d"));369 370 Date = DateTime.Now.AddDays(-BackDay);371 Start_Time = new DateTime(Date.Year, Date.Month, Date.Day, 0, 0, 0, 0);372 break;373 374 #endregion375 376 #region =====上周=====377 378 case DateSection.LastWeek:379 BackDay = Convert.ToInt32(Date.DayOfWeek.ToString("d")) + 1;380 381 // 到上周最后一天382 Date = DateTime.Now.AddDays(-BackDay);383 End_Time = new DateTime(Date.Year, Date.Month, Date.Day, 23, 59, 59, 999);384 385 // 到上周第一天386 Date = Date.AddDays(-6);387 Start_Time = new DateTime(Date.Year, Date.Month, Date.Day, 0, 0, 0, 0);388 389 break;390 391 #endregion392 393 #region =====本月=====394 395 case DateSection.ThisMonth:396 End_Time = Date;397 Start_Time = new DateTime(Date.Year, Date.Month, 1, 0, 0, 0, 0);398 break;399 400 #endregion401 402 #region =====上月=====403 404 case DateSection.LastMonth:405 406 BackDay = Date.Day;407 408 // 到上月最后一天409 Date = DateTime.Now.AddDays(-BackDay);410 End_Time = new DateTime(Date.Year, Date.Month, Date.Day, 23, 59, 59, 999);411 412 Start_Time = new DateTime(Date.Year, Date.Month, 1, 0, 0, 0, 0);413 break;414 415 #endregion416 417 #region =====今年=====418 419 case DateSection.ThisYear:420 End_Time = Date;421 Start_Time = new DateTime(Date.Year, 1, 1, 0, 0, 0, 0);422 break;423 424 #endregion425 426 #region =====去年=====427 428 case DateSection.LastYear:429 BackDay = Date.DayOfYear;430 431 // 到去年最后一天432 Date = DateTime.Now.AddDays(-BackDay);433 End_Time = new DateTime(Date.Year, Date.Month, Date.Day, 23, 59, 59, 999);434 435 Start_Time = new DateTime(Date.Year, 1, 1, 0, 0, 0, 0);436 break;437 438 #endregion439 440 default: break;441 }442 443 }444 445 ///446 /// 保证开始和结束时间绝对不为空447 /// 448 /// 间隔长度,默认:7449 /// 间隔单位,默认:Day(天)450 public void GetDateNow(int DateLong = 7, DateFormat dateFormat = DateFormat.Day) {451 452 // 校验是否存在此枚举453 if (Enum.IsDefined(typeof(DateFormat), (int)dateFormat))454 dateFormat = DateFormat.Day;455 456 // 初始化结束时间457 if (End_Time == null)458 End_Time = DateTime.Now;459 460 DateTime? star;461 462 // 有校验的时间463 star = new DateTime();464 star = Start_Time;465 ChangStar(ref star, End_Time, DateLong, dateFormat);466 Start_Time = star;467 468 }469 470 ///471 /// 根据结束时间,修改开始时间472 /// 若开始时间有值,则不改动473 /// 474 /// 开始时间475 /// 结束时间476 /// 间隔长度477 /// 间隔单位478 private void ChangStar(ref DateTime? Start, DateTime? End, int DateLong, DateFormat dateFormat) {479 480 if (Start.HasValue)481 return;482 483 DateLong = 0 - DateLong;484 485 // 获取开始时间486 switch (dateFormat) {487 // 年份488 case DateFormat.Year:489 Start = End.Value.AddYears(DateLong);490 break;491 // 月份492 case DateFormat.Month:493 Start = End.Value.AddMonths(DateLong);494 break;495 // 天数496 case DateFormat.Day:497 Start = End.Value.AddDays(DateLong);498 break;499 // 小时500 case DateFormat.Hour:501 Start = End.Value.AddHours(DateLong);502 break;503 // 分钟504 case DateFormat.Minute:505 Start = End.Value.AddMinutes(DateLong);506 break;507 // 秒钟508 case DateFormat.Second:509 Start = End.Value.AddSeconds(DateLong);510 break;511 }512 513 }514 515 #endregion516 }517 518 ///519 /// 时间格式520 /// 521 public enum DateFormat {522 ///523 /// 年份524 /// 525 Year,526 ///527 /// 月份528 /// 529 Month,530 ///531 /// 天数532 /// 533 Day,534 ///535 /// 小时536 /// 537 Hour,538 ///539 /// 分钟540 /// 541 Minute,542 ///543 /// 秒钟544 /// 545 Second546 }547 548 ///549 /// 时间区间550 /// 551 public enum DateSection {552 ///553 /// 今天554 /// 555 Today,556 ///557 /// 昨天558 /// 559 Yesterday,560 ///561 /// 本周,星期天为第一天562 /// 563 ThisWeek,564 ///565 /// 上周,星期天为第一天566 /// 567 LastWeek,568 ///569 /// 本月570 /// 571 ThisMonth,572 ///573 /// 上月574 /// 575 LastMonth,576 ///577 /// 今年578 /// 579 ThisYear,580 ///581 /// 去年582 /// 583 LastYear584 }585 586 }
个人觉着,虽然代码一般般,但里面有不少小技巧还是很不错的,适合那些比我还新的新手学习一下,比如时间校验,,那个真的是伤透了心
如果大家发现了有什么bug,欢迎指出,或者有比较有趣的点子也欢迎互相交流,,,嗯,就酱紫,我去纠结TreeView控件绑值的问题,,