之前發短信使用太短信貓,如今,更多地是使用第三方API。大體過程是:javascript
→ 用戶在頁面輸入手機號碼
→ 用戶點擊"獲取驗證碼"按鈕,把手機號碼發送給服務端,服務端產生幾位數的隨機碼,並保存在某個地方(Session, Applicaiton, 數據庫, 等等),調用第三方的API
→ 第三方發送幾位數的隨機碼至用戶手機
→ 用戶在頁面輸入接收到的隨機碼
→ 把隨機碼等發送給服務端,與服務端保存的隨機碼比較,若是一致,就經過,讓註冊php
就按以下界面來講吧:html
咱們須要考慮的方面包括:前端
● 手機號碼:判斷手機號碼的合法性,與數據庫中已有的手機號碼比較,判斷是否有重複,等等
● "獲取短信驗證碼"按鈕:點擊後,禁用它,再來一個好比60秒的倒計時,倒計時結束恢復使用
● "提交"按鈕:在提交以前須要判斷表單是否驗證經過,以及驗證碼是否經過
● 點擊"獲取短信驗證碼"按鈕的次數:好比,須要限制來自同一個IP,天天只能點擊這個按鈕3次java
選擇驗證碼提供方、前期準備ajax
本人選擇了"雲之訊":http://www.ucpaas.com/數據庫
註冊成爲"雲之訊"的用戶。json
進入"雲之訊"管理後臺,首頁就可看到開發者信息,包括Account Sid, AuthToken, Rest URL,這些未來都會用到。api
依次點擊"應用管理","應用列表",右側頁面的"建立應用",建立成功後將會分配到一個應用ID,這個應用ID也會被用到。另外,建立的應用須要"雲之訊"審覈經過後,才能夠在本地調試。app
依次點擊"應用管理","短信管理",右側頁面的"添加模版", 在"添加模版"頁,"內容"這項應該相似這樣填寫:您註冊{1}網站的驗證碼爲{2},請於{3}分鐘內正確輸入驗證碼,添加短信模版成功後會分配到一個模版ID,這個模版ID也會被用到,建立的短信模版也須要"雲之訊"審覈經過後,才能夠在本地調試。
好了,再來總結一下,咱們在開發的時候須要哪些信息。包括:
● Rest URL
● Account Sid
● AuthToken
● 應用ID
● 短信模版ID
● 短息模版的內容,好比"您註冊{1}網站的驗證碼爲{2},請於{3}分鐘內正確輸入驗證碼",{1},{2},{3}是佔位符,在應用程序中咱們只須要拼接出一個以英文逗號隔開的字符串就能夠,好比"個人網站,個人驗證碼,1"
先下載一個C#的Demo,在這裏下載:http://www.ucpaas.com/product_service/download, 在"REST Server Demo"中能夠找到。
另外,"雲之訊"C#版Demo是使用HttpWebRequest,向API發出請求的,在ASP.NET MVC中,咱們也可使用HttpClient向API發出請求,請求格式參考以下:
http://docs.ucpaas.com/doku.php?id=rest_api%E4%BB%8B%E7%BB%8D
http://docs.ucpaas.com/doku.php?id=%E7%9F%AD%E4%BF%A1%E9%AA%8C%E8%AF%81%E7%A0%81_%E6%A8%A1%E6%9D%BF%E7%9F%AD%E4%BF%A1
在開發的時候,有時須要查看返回的狀態碼,查看這裏:http://docs.ucpaas.com/doku.php?id=rest_error
在ASP.NET MVC下開發
對於用戶註冊相關的視圖模型,給出以下一個類。
public class UserInputVm{[Required(ErrorMessage = "必填")][StringLength(16, ErrorMessage = "長度1-16位")][Display(Name = "請輸入手機號")][RegularExpression(@"^1[3458][0-9]{9}$", ErrorMessage = "手機號格式不正確")]public string LoginName { get; set; }}
以上,只考慮了手機輸入的合法性,現實中,還須要判斷用戶輸入的手機號是否和數據庫中已有的重複,用到一個遠程驗證特性,參考這裏:http://www.cnblogs.com/darrenji/p/3578133.html
在項目根目錄下建立一個"Extension"文件夾,並建立一個UCSRestRequest類,把"雲之訊"C#版Demo中的UCSRestRequest類下的內容以及EBodyType枚舉一同拷貝下來。
在HomeController下,Index方法送出一個視圖模型實例。
public class HomeController : Controller{public ActionResult Index(){return View(new UserInputVm());}......}
在Home/Index.cshtml視圖中,點擊"發送驗證碼"按鈕,讓該按鈕倒計時,並把手機號發送給服務端;點擊"提交"按鈕,判斷驗證表單經過以後,再向服務端發送用戶填寫的驗證碼,驗證碼經過以後才提交表單信息。
@model MvcApplication2.Models.UserInputVm@{ViewBag.Title = "Index";Layout = "~/Views/Shared/_Layout.cshtml";}<h2>Index</h2>@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { id = "addForm" })){@Html.LabelFor(m => m.LoginName)@Html.TextBoxFor(m => m.LoginName)@Html.ValidationMessageFor(m => m.LoginName)<span id="success"></span><br /><br /><span>請輸入驗證碼</span><input type="text" id="myCode" /><span id="codehint"></span><input type="button" id="getCode" value="點擊獲取驗證碼" /><br /><br /><input type="button" id="up" value="提交" />}@section scripts{<script type="text/javascript">$(function () {//點擊發送驗證碼$('#getCode').on("click", function () {checkGetCodeBtn();$.post('@Url.Action("GetCheckNum", "Home")', { 'phoneNum': $('#LoginName').val() }, function (data) {if (data.msg) {$('#success').text("已發送驗證碼");alert(data.content);} else {var $getCodeBtn = $('#getCode');clearInterval(t);$getCodeBtn.prop('disabled', false);$getCodeBtn.val("點擊獲取驗證碼");count = 60;$('#success').text("");}});});//提交$('#up').on("click", function () {//表單驗證經過才驗證驗證碼的正確性if ($('#addForm').valid()) {//clearInterval(t);$.post('@Url.Action("CheckCode", "Home")', { 'checkNum': $('#myCode').val() }, function (data) {if (data.msg) {//驗證碼匹配alert(data.content);$.ajax({cache: false,url: '@Url.Action("Index", "Home")',type: 'POST',dataType: 'json',data: $('#addForm').serialize(),success: function (result) {if (result.msg) {alert(result.content);}},error: function (xhr, status) {alert("提交失敗,狀態碼:" + status);}});} else {$('#codehint').text(data.content);}});}});});var count = 60; //計時開始var t; //時間間隔種子var isPass = false;//驗證碼是否輸入正確function checkGetCodeBtn() {//關於按鈕var $getCodeBtn = $('#getCode');t = setInterval(function () {$getCodeBtn.val(count + "秒以後從新獲取");$getCodeBtn.prop('disabled', true);count--;if (count == 0) {clearInterval(t);$getCodeBtn.prop('disabled', false);$getCodeBtn.val("點擊獲取驗證碼");count = 60;$('#success').text("");}}, 1000);}</script>}
再回到HomeController,有一個方法用來接收用戶的手機號,產生並保存隨機碼,調用API;有一個方法用來接收用戶的短信驗證碼,判斷是否匹配;固然還有一個接收表單數據的方法。
public class HomeController : Controller{......[HttpPost]public ActionResult Index(UserInputVm userInputVm){if (ModelState.IsValid){//實際上這裏要作數據庫保存工做//return PartialView("DisplayUser", userInputVm);return Json(new {msg=true,content=userInputVm.LoginName});}else{return View(userInputVm);}}//讓api把驗證碼發給用戶手機[HttpPost]//[EnableThrottling(PerSecond = 1, PerMinute = 1, PerHour = 3, PerDay = 3)]public ActionResult GetCheckNum(string phoneNum){//TODO: 在這裏再次判斷用戶手機號是否與數據庫中已有的重複//產生隨機驗證碼Random r = new Random();string temMsg = string.Empty;for (int i = 0; i < 4; i++)
{temMsg += r.Next(0, 9);}//保存隨機碼//Session["num"] = temMsg;//ControllerContext.HttpContext.Application["num"] = temMsg;//TODO: 這裏建議隨機碼保存到數據庫,由於調用API發送短信,Session,Application狀態會丟失//拼接短信內容StringBuilder sb = new StringBuilder();sb.AppendFormat("{0},{1},{2}", "就依你", temMsg, "1"); //您註冊{1}網站的驗證碼爲{2},請於{3}分鐘內正確輸入驗證碼#region Demostring serverIp = "api.ucpaas.com";string serverPort = "443";string account = "這裏填寫開發者的Account Sid"; //用戶sidstring token = "這裏填寫開發者的AuthToken"; //用戶sid對應的tokenstring appId = "這裏填寫審覈經過上線應用的Id"; //對應的應用id,非測試應用需上線使用string templatedId = "這裏填寫短信模版ID"; //短信模板id,需經過審覈//string clientNum = "60000000000001";//string clientpwd = "";//string friendName = "";//string clientType = "0";//string charge = "0";//string phone = "";//string date = "day";//uint start = 0;//uint limit = 100;//string toPhone = ""; //發送短信手機號碼,羣發逗號區分//string param = ""; //短信參數 a,b,c//string verifyCode = "1234";//string fromSerNum = "4000000000";//string toSerNum = "4000000000";//string maxallowtime = "60";UCSRestRequest api = new UCSRestRequest();api.init(serverIp, serverPort);api.setAccount(account, token);api.enabeLog(true);api.setAppId(appId);api.enabeLog(true);string feedback = api.SendSMS(phoneNum, templatedId, sb.ToString());#endregionreturn Json(new { msg = true, content = feedback });}//檢測驗證碼[HttpPost]public ActionResult CheckCode(string checkNum){try{//實際須要從數據庫中獲取保存的隨機短信驗證碼//實際,這裏的Application["num"]或Session["num"]數據狀態已經丟失了if (ControllerContext.HttpContext.Application["num"] != null){string temp = ControllerContext.HttpContext.Application["num"].ToString();if (checkNum == temp){return Json(new { msg = true, content = temp });}else{return Json(new { msg = false, content = "驗證碼不匹配請從新輸入" });}}else{return Json(new { msg = false, content = "驗證碼失效請從新獲取" });}}catch (Exception ex){throw;}}}
以上,
● [HttpPost]下的Index方法,用來接收保存表單數據。
● GetCheckNum方法接收用戶手機號,須要提醒的是:
一、EnableThrottling特性是用來限制在單位時間間隔以內,來自同一個IP請求的次數,使用方法參考這裏:http://www.cnblogs.com/darrenji/p/4446767.html
二、不要把隨機短信驗證碼保存到Session或Application中,由於Demo中實際使用HttpWebRequest向API發出請求,另開了一個線程,Session或Application狀態會丟失,建議把隨機短信驗證碼保存到數據庫中。
三、雖然前端頁面中能夠判斷用戶輸入手機號是否與數據庫中已有的重複,但用戶仍是有可能把重複的手機號發送到服務端來,因此,在服務端的本方法內,仍是有必要再次判斷用戶的手機號是否重複。
四、也能夠不使用Demo中提供的方法向API發出請求,使用HttpClient一樣能夠,不過須要字節拼接和設置,須要對HttpClient的使用有必定的瞭解。
● CheckCode方法用來接收用戶的短信驗證碼,建議把接收的驗證碼和數據庫中該手機號的驗證碼對比。
最後,♥謝謝UIT工做室推薦了"雲之訊"並提供了HttpClient調用的代碼參考,♥謝謝"雲之訊"團隊成員的配合,他/她們是:Cathy@雲之訊,Ada@雲之訊, Auspicious@雲之訊,等等♥。