MVC查询数据接收及校验
本来想写一篇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 /// <summary> 9 /// 用户类 10 /// </summary> 11 public class User { 12 13 /// <summary> 14 /// 用户姓名 15 /// </summary> 16 public string userName; 17 18 /// <summary> 19 /// 用户类型 20 /// </summary> 21 public int type; 22 23 /// <summary> 24 /// 身份证号 25 /// </summary> 26 public string code; 27 28 /// <summary> 29 /// 用户身高 30 /// </summary> 31 public int height; 32 33 /// <summary> 34 /// 用户性别 35 /// </summary> 36 public string sex; 37 38 #region 注册时间范围 39 40 public DateTime startTime; 41 public DateTime endTime; 42 43 #endregion 44 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 /// <summary> 6 /// 用于接收前台传过来的数据 7 /// </summary> 8 public struct ReqData { 9 10 #region 分页数据 11 12 /// <summary> 13 /// 数据总行数 14 /// </summary> 15 public int PageCount { get; set; } 16 17 /// <summary> 18 /// 当前页码 19 /// </summary> 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 /// <summary> 33 /// 每页行数 34 /// </summary> 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 /// <summary> 48 /// 页面跳转链接,带参数 49 /// 用于分页跳转 50 /// </summary> 51 public string PageUrl { get; set; } 52 53 /// <summary> 54 /// 页面跳转链接,不带参数 55 /// 用于删除时跳转 56 /// </summary> 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 /// <summary> 77 /// 视图样式,{ txt:列表视图,img:图片视图 } 78 /// </summary> 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 /// <summary> 94 /// 导航栏标题 95 /// </summary> 96 public string Title { 97 get { return CheckStr(ref title); } 98 set { title = value; } 99 } 100 private string title; 101 102 #endregion 103 104 #region 查询参数 105 106 /// <summary> 107 /// 用户编号 108 /// </summary> 109 public int? ID { 110 get { return id; } 111 set { id = value; } 112 } 113 private int? id; 114 115 /// <summary> 116 /// 用户名 117 /// </summary> 118 public string UserName { 119 get { return CheckStr(ref userName); } 120 set { userName = value; } 121 } 122 private string userName; 123 124 /// <summary> 125 /// 用户等级,范围:[0,3] 126 /// </summary> 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 /// <summary> 139 /// 开始时间,时分秒为 0:0:0,并且不能大于End_Time 140 /// </summary> 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 /// <summary> 166 /// 结束时间,时分秒为 23:59:59,并且不能大于当前时间 167 /// </summary> 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 else 196 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 #endregion 206 207 #region 时间输出 208 209 /// <summary> 210 /// 时间字符串返回格式 211 /// 若不设置,默认为"yyyy-MM-dd HH:mm:ss" 212 /// </summary> 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 /// <summary> 224 /// 用于返回开始时间字符串 225 /// </summary> 226 public string GetStarTimeStr { 227 get { 228 if (Start_Time.HasValue) 229 return Start_Time.Value.ToString(Format); 230 return ""; 231 } 232 } 233 234 /// <summary> 235 /// 用于返回结束时间字符串 236 /// </summary> 237 public string GetEndTimeStr { 238 get { 239 if (End_Time.HasValue) 240 return End_Time.Value.ToString(Format); 241 return ""; 242 } 243 } 244 245 #endregion 246 247 #endregion 248 249 #endregion 250 251 #region 校验方法 252 253 /// <summary> 254 /// 保证num的值范围为,[min,max] 255 /// </summary> 256 /// <param name="num">原始数据</param> 257 /// <param name="min">最小值</param> 258 /// <param name="max">最大值</param> 259 /// <param name="def">默认值(不填时,默认值为最小值)</param> 260 /// <returns></returns> 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 /// <summary> 276 /// 将字符串去掉空格,并进行敏感字符检测 277 /// </summary> 278 /// <param name="str">原字符串</param> 279 /// <param name="Ischeck">是否开启敏感字符校验</param> 280 /// <param name="def">默认的值,字符串为空时,赋此值</param> 281 /// <returns></returns> 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 /// <summary> 293 /// 检测是否含有危险字符(防止Sql注入) 294 /// 转自:http://blog.csdn.net/chaozi/article/details/4462312 295 /// </summary> 296 /// <param name="contents">预检测的内容</param> 297 /// <returns>返回True或false</returns> 298 public static bool GetIsFormText(string contents) { 299 bool bReturnValue = false; 300 if (contents.Length > 0) { 301 //convert to lower 302 string sLowerStr = contents.ToLower(); 303 //RegularExpressions 304 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 //Match 308 bool bIsMatch = false; 309 System.Text.RegularExpressions.Regex sRx = new 310 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 #endregion 321 322 #region 数据绑定方法 323 324 /// <summary> 325 /// 返回指定区间的日期,默认今天 326 /// </summary> 327 /// <param name="dateSection"></param> 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 #endregion 350 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 #endregion 361 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 #endregion 375 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 #endregion 392 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 #endregion 401 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 #endregion 416 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 #endregion 425 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 #endregion 439 440 default: break; 441 } 442 443 } 444 445 /// <summary> 446 /// 保证开始和结束时间绝对不为空 447 /// </summary> 448 /// <param name="DateLong">间隔长度,默认:7</param> 449 /// <param name="dateFormat">间隔单位,默认:Day(天)</param> 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 /// <summary> 471 /// 根据结束时间,修改开始时间 472 /// 若开始时间有值,则不改动 473 /// </summary> 474 /// <param name="Start">开始时间</param> 475 /// <param name="End">结束时间</param> 476 /// <param name="DateLong">间隔长度</param> 477 /// <param name="dateFormat">间隔单位</param> 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 #endregion 516 } 517 518 /// <summary> 519 /// 时间格式 520 /// </summary> 521 public enum DateFormat { 522 /// <summary> 523 /// 年份 524 /// </summary> 525 Year, 526 /// <summary> 527 /// 月份 528 /// </summary> 529 Month, 530 /// <summary> 531 /// 天数 532 /// </summary> 533 Day, 534 /// <summary> 535 /// 小时 536 /// </summary> 537 Hour, 538 /// <summary> 539 /// 分钟 540 /// </summary> 541 Minute, 542 /// <summary> 543 /// 秒钟 544 /// </summary> 545 Second 546 } 547 548 /// <summary> 549 /// 时间区间 550 /// </summary> 551 public enum DateSection { 552 /// <summary> 553 /// 今天 554 /// </summary> 555 Today, 556 /// <summary> 557 /// 昨天 558 /// </summary> 559 Yesterday, 560 /// <summary> 561 /// 本周,星期天为第一天 562 /// </summary> 563 ThisWeek, 564 /// <summary> 565 /// 上周,星期天为第一天 566 /// </summary> 567 LastWeek, 568 /// <summary> 569 /// 本月 570 /// </summary> 571 ThisMonth, 572 /// <summary> 573 /// 上月 574 /// </summary> 575 LastMonth, 576 /// <summary> 577 /// 今年 578 /// </summary> 579 ThisYear, 580 /// <summary> 581 /// 去年 582 /// </summary> 583 LastYear 584 } 585 586 }
个人觉着,虽然代码一般般,但里面有不少小技巧还是很不错的,适合那些比我还新的新手学习一下,比如时间校验,,那个真的是伤透了心
如果大家发现了有什么bug,欢迎指出,或者有比较有趣的点子也欢迎互相交流,,,嗯,就酱紫,我去纠结TreeView控件绑值的问题,,