爱上MVC3系列~无刷新验证码
在MVC中进行留言,评论等功能时,不可避免会用到表单提交时的验证码问题,有时,我们的作法是,当表单被提交后,在controller里去判断验证码的正确与否,但我认为这种用户体验是很差的,今天正好有后时间,把这方法的东西总结一下:
首先,在公用项目中建立一个生成图片验证码的类型ValidateCode,代码如下:
1 /// <summary> 2 /// 生成验证码对象 3 /// </summary> 4 public class ValidateCode 5 { 6 public ValidateCode() 7 { 8 } 9 ///<summary> 10 /// 验证码的最大长度 11 ///</summary> 12 public int MaxLength 13 { 14 get { return 10; } 15 } 16 ///<summary> 17 /// 验证码的最小长度 18 ///</summary> 19 public int MinLength 20 { 21 get { return 1; } 22 } 23 ///<summary> 24 /// 生成验证码 25 ///</summary> 26 ///<param name="length">指定验证码的长度</param> 27 ///<returns></returns> 28 public string CreateValidateCode(int length) 29 { 30 int[] randMembers = new int[length]; 31 int[] validateNums = new int[length]; 32 string validateNumberStr = ""; 33 //生成起始序列值 34 int seekSeek = unchecked((int)DateTime.Now.Ticks); 35 Random seekRand = new Random(seekSeek); 36 int beginSeek = (int)seekRand.Next(0, Int32.MaxValue - length * 10000); 37 int[] seeks = new int[length]; 38 for (int i = 0; i < length; i++) 39 { 40 beginSeek += 10000; 41 seeks[i] = beginSeek; 42 } 43 //生成随机数字 44 for (int i = 0; i < length; i++) 45 { 46 Random rand = new Random(seeks[i]); 47 int pownum = 1 * (int)Math.Pow(10, length); 48 randMembers[i] = rand.Next(pownum, Int32.MaxValue); 49 } 50 //抽取随机数字 51 for (int i = 0; i < length; i++) 52 { 53 string numStr = randMembers[i].ToString(); 54 int numLength = numStr.Length; 55 Random rand = new Random(); 56 int numPosition = rand.Next(0, numLength - 1); 57 validateNums[i] = Int32.Parse(numStr.Substring(numPosition, 1)); 58 } 59 //生成验证码 60 for (int i = 0; i < length; i++) 61 { 62 validateNumberStr += validateNums[i].ToString(); 63 } 64 return validateNumberStr; 65 } 66 ///<summary> 67 /// 创建验证码的图片 68 ///</summary> 69 ///<param name="containsPage">要输出到的page对象</param> 70 ///<param name="validateNum">验证码</param> 71 public byte[] CreateValidateGraphic(string validateCode) 72 { 73 Bitmap image = new Bitmap((int)Math.Ceiling(validateCode.Length * 14.0), 22); 74 Graphics g = Graphics.FromImage(image); 75 try 76 { 77 //生成随机生成器 78 Random random = new Random(); 79 //清空图片背景色 80 g.Clear(Color.White); 81 //画图片的干扰线 82 for (int i = 0; i < 25; i++) 83 { 84 int x1 = random.Next(image.Width); 85 int x2 = random.Next(image.Width); 86 int y1 = random.Next(image.Height); 87 int y2 = random.Next(image.Height); 88 g.DrawLine(new Pen(Color.Silver), x1, y1, x2, y2); 89 } 90 Font font = new Font("Arial", 14, (FontStyle.Bold | FontStyle.Italic)); 91 LinearGradientBrush brush = new LinearGradientBrush(new Rectangle(0, 0, image.Width, image.Height), 92 Color.Blue, Color.DarkRed, 1.5f, true); 93 g.DrawString(validateCode, font, brush, 3, 2); 94 //画图片的前景干扰点 95 for (int i = 0; i < 100; i++) 96 { 97 int x = random.Next(image.Width); 98 int y = random.Next(image.Height); 99 image.SetPixel(x, y, Color.FromArgb(random.Next())); 100 } 101 //画图片的边框线 102 g.DrawRectangle(new Pen(Color.Silver), 0, 0, image.Width - 1, image.Height - 1); 103 //保存图片数据 104 MemoryStream stream = new MemoryStream(); 105 image.Save(stream, ImageFormat.Jpeg); 106 //输出图片流 107 return stream.ToArray(); 108 } 109 finally 110 { 111 g.Dispose(); 112 image.Dispose(); 113 } 114 } 115 }
然后,在当前公用的controller中添加一个返回文件类型的action,当然也可以是所有返回类型的基类ActionResult,这个方法用来返回一个图像对象
1 /// <summary> 2 /// 生成验证码图像对象 3 /// </summary> 4 /// <returns></returns> 5 public ActionResult GetValidateCode() 6 { 7 ValidateCode vCode = new ValidateCode(); 8 string code = vCode.CreateValidateCode(4); 9 Session["ValidateCode"] = code; 10 byte[] bytes = vCode.CreateValidateGraphic(code); 11 return File(bytes, @"image/jpeg"); 12 }
再次,需要我们写一个返回当前验证吗的方法,这是作无刷新验证码的关键
1 /// <summary> 2 /// 得到当前验证码 3 /// </summary> 4 /// <returns></returns> 5 public ActionResult GetCurrentValidateCode() 6 { 7 return Content(Session["ValidateCode"].ToString()); 8 }
最后,就是页面上的事了,看代码:
1 <div id="ValidateCodeSpan"> 2 请输入验证码:@Html.TextBox("VCode") 3 <img id="valiCode" style="cursor: pointer;" src="/Common/GetValidateCode" alt="看不清?请点我" /> 4 @Html.Hidden("ValidateCode") 5 <script type="text/javascript"> 6 $(function () { 7 //首次加载 8 $("#valiCode").attr("src","/Common/GetValidateCode?time=" + (new Date()).getTime()); 9 $.get("/Common/GetCurrentValidateCode", function (data) { 10 $("#ValidateCode").val(data); 11 }); 12 //单击验证码事件 13 $("#valiCode").bind("click", function () { 14 this.src = "/Common/GetValidateCode?time=" + (new Date()).getTime(); 15 $.get("/Common/GetCurrentValidateCode", function (data) { 16 $("#ValidateCode").val(data); 17 }); 18 }); 19 }); 20 </script> 21 </div>