AspNetCore - MVC實戰系列目錄 . 愛留圖網站誕生 . AspNetCore - MVC實戰系列(一)之Sqlserver表映射實體模型 . AspNetCore-MVC實戰系列(二)之通過綁定郵箱找回密碼 開篇嘮嗑 本篇內容寫在5.1假期前夕,主要是讓大家能在節假日休息充點的時候能有 ...
AspNetCore - MVC實戰系列目錄
. 愛留圖網站誕生
. AspNetCore - MVC實戰系列(一)之Sqlserver表映射實體模型
. AspNetCore-MVC實戰系列(二)之通過綁定郵箱找回密碼
開篇嘮嗑
本篇內容寫在5.1假期前夕,主要是讓大家能在節假日休息充點的時候能有好的乾貨例子,到目前為止netcore方面的實戰例子分享即將進入正軌,謝謝各位朋友多多支持;最近工作安排的新項目即將開始,項目前期就我一人搭建,讓我猶豫的是對於公司這個內部系統並且是初建的項目用什麼開發方式好呢,最初考慮的是mvc5但是又想了下如果這樣還不如直接使用NetCore1.1的MVC呢,因為現在這版本基本也算穩定了可以試試水,可是又有顧慮是mvc項目在上線的時候會影響到其他人的使用(前期不考慮nginx分發),然後目光又轉向aspx網站的方式,不得不說這種方式在發佈上的確有優勢,尤其是在沒有分散式的前提下;好吧目前還在考慮中,希望能得到各位朋友的建議。。。
註冊模塊
首先,這裡講解的內容對應的實體和表結構是基於上一篇文章創建的項目這裡就不多說了;對於一個註冊功能來說,通常需要的屬性是:賬號,密碼,確認密碼,驗證碼(可省略),甚至有些快捷的註冊方式就是通過手機號來註冊,當然咋們沒有簡訊通道的功能不能發簡訊,所以採用前者,先來看下Action中設計代碼:
Register的get路由Action
1 // GET: Member/Create 2 public IActionResult Register() 3 { 4 return View(); 5 }
Register的post提交註冊信息的Action
1 [HttpPost] 2 [ValidateAntiForgeryToken] 3 public async Task<IActionResult> Register([Bind("UserName,UserPwd,ComfirmPwd")] MoRegisterUser loginUser) 4 { 5 if (ModelState.IsValid) 6 { 7 #region 驗證 8 if (_context.ToUserInfo.Any(b => b.UserName.ToUpper() == loginUser.UserName.Trim().ToUpper())) 9 { 10 this.MsgBox("已經存在相同的賬號!"); 11 return View(loginUser); 12 } 13 #endregion 14 15 #region 入庫 16 17 ToUserInfo userInfo = new ToUserInfo(); 18 19 userInfo.UserName = loginUser.UserName.Trim(); 20 userInfo.UserPwd = PublicClass._Md5(loginUser.UserPwd.Trim()); 21 userInfo.NickName = userInfo.UserName; 22 userInfo.Status = (int)EnumHelper.EmUserStatus.啟用; 23 userInfo.CreateTime = DateTime.Now; 24 userInfo.LevelNum = (int)EmLevelNum.註冊; 25 26 userInfo.Ips = this.GetUserIp(); 27 userInfo.HeadPhoto = "/images/ailiutu_user.png"; 28 userInfo.Sex = false; 29 30 _context.Add(userInfo); 31 var result = await _context.SaveChangesAsync(); 32 if (result > 0) 33 { 34 var moUserInfo = new MoUserInfo 35 { 36 Id = userInfo.Id, 37 UserName = userInfo.UserName, 38 NickName = userInfo.NickName, 39 Addr = userInfo.Addr, 40 Birthday = userInfo.Birthday, 41 42 Blog = userInfo.Blog, 43 CreateTime = userInfo.CreateTime, 44 Email = userInfo.Email, 45 HeadPhoto = userInfo.HeadPhoto, 46 Introduce = userInfo.Introduce, 47 48 Ips = userInfo.Ips, 49 LevelNum = userInfo.LevelNum, 50 Sex = userInfo.Sex, 51 Tel = userInfo.Tel, 52 Status = userInfo.Status, 53 54 LoginTime = DateTime.Now 55 }; 56 HttpContext.Session.Set<MoUserInfo>(HttpContext.Session.SessionKey(), moUserInfo); 57 58 if (!string.IsNullOrWhiteSpace(moUserInfo.Ips)) 59 { 60 _context.ToUserLog.Add(new ToUserLog 61 { 62 CodeId = (int)EmLogCode.登錄, 63 CreateTime = DateTime.Now, 64 Des = $"IP:{moUserInfo.Ips},登錄時間:{moUserInfo.LoginTime.ToString("yyyy-MM-dd HH:mm")}", 65 UserId = userInfo.Id 66 }); 67 } 68 69 _context.ToUserLog.Add(new ToUserLog 70 { 71 CodeId = (int)EmLogCode.積分, 72 CreateTime = DateTime.Now, 73 Des = $"【註冊】 +{(int)EmLevelNum.註冊}", 74 UserId = userInfo.Id 75 }); 76 await _context.SaveChangesAsync(); 77 78 return RedirectToAction(nameof(HomeController.Index), "home"); 79 } 80 #endregion 81 82 this.MsgBox("註冊失敗,請稍後重試。"); 83 return View(loginUser); 84 } 85 return View(loginUser); 86 }
通過Post的Action能夠看出註冊處理的Action主要操作步驟有以下幾點:
. ModelState.IsValid驗證提交的信息是否滿足model規則設置
. Linq的Any()方法驗證是否存在相同賬號
. _context.Add()入庫註冊信息
. HttpContext.Session.Set的擴展方法設置登陸的session
. 記錄登陸記錄和登陸增加的積分信息
對於一個簡單的註冊基本就是這樣的流程,我們來看看提交註冊時的模型實體:
1 /// <summary> 2 /// 註冊實體 3 /// </summary> 4 public class MoRegisterUser 5 { 6 [Required(AllowEmptyStrings = false, ErrorMessage = "賬號長度範圍6-30字元!")] 7 [Display(Prompt = "郵箱/手機號/6-30字元")] 8 [RegularExpression(@"[^\s]{6,30}", ErrorMessage = "賬號長度範圍6-30字元。")] 9 public string UserName { get; set; } 10 11 [Required(AllowEmptyStrings = false, ErrorMessage = "密碼長度範圍6-20字元!")] 12 [DataType(DataType.Password)] 13 [Display(Prompt = "密碼長度範圍6-20字元!")] 14 [RegularExpression(@"[^\s]{6,20}", ErrorMessage = "密碼長度範圍6-20字元。")] 15 public string UserPwd { get; set; } 16 17 [Compare("UserPwd", ErrorMessage = "密碼與確認密碼不相同!")] 18 [DataType(DataType.Password)] 19 [Display(Prompt = "必須與密碼相同")] 20 public string ComfirmPwd { get; set; } 21 }
這裡自定義的註冊模型,設置了DataAnnotations,以此來快速設置驗證,不用再每個都用js寫了,mvc框架幫你做了這些;下麵看看View的代碼:
1 @model LovePicture.Model.MoClass.MoRegisterUser 2 3 @{ 4 ViewData["Title"] = "註冊"; 5 } 6 7 <h3><span class="glyphicon glyphicon-flag" aria-hidden="true"></span> 註冊</h3> 8 <form name="form_submit" asp-action="Register"> 9 <div class="form-horizontal"> 10 <h4> 愛留圖:歡迎您成為我們的一份子,讓我們一起留存珍惜的圖片吧。</h4> 11 <hr /> 12 <div asp-validation-summary="ModelOnly" class="text-danger"></div> 13 <div class="form-group"> 14 <label asp-for="UserName" class="col-md-2 control-label">賬號</label> 15 <div class="col-md-10"> 16 <input asp-for="UserName" required="required" class="form-control" /> 17 <span asp-validation-for="UserName" class="text-danger"></span> 18 </div> 19 </div> 20 <div class="form-group"> 21 <label asp-for="UserPwd" class="col-md-2 control-label">密碼</label> 22 <div class="col-md-10"> 23 <input asp-for="UserPwd" required="required" class="form-control" /> 24 <span asp-validation-for="UserPwd" class="text-danger"></span> 25 </div> 26 </div> 27 <div class="form-group"> 28 <label asp-for="ComfirmPwd" class="col-md-2 control-label">確認密碼</label> 29 <div class="col-md-10"> 30 <input asp-for="ComfirmPwd" class="form-control" /> 31 <span asp-validation-for="ComfirmPwd" class="text-danger"></span> 32 </div> 33 </div> 34 35 <div class="form-group"> 36 <div class="col-md-offset-2 col-md-10"> 37 <input type="button" value="註 冊" name="btnSubmit" class="btn btn-default" /> 38 <span id="msgbox" style="color:red">@ViewData["msgbox"]</span> 39 </div> 40 </div> 41 </div> 42 </form> 43 <br /> 44 <div> 45 <a href="/member/login">有賬號去登錄</a> | <a href="/member/ForgetPassword">忘記密碼?</a> 46 </div> 47 <script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script> 48 <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>View Code
對於mvc模型註解的方式在前端需要引入這兩個js文件
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script> <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
View內容註意點在於我試圖中的Button按鈕是不是submit形式,這樣做的理由是,當您註冊的Action業務過多時,用戶點擊註冊按鈕提交數據,這個時候如果無法快速響應信息給用戶,那麼用戶可能多次點擊,因此就有個需求是需要吧註冊提交按鈕置灰或者影藏點,這裡我為了方便把全站點的提交按鈕都弄成統一name的按鈕了,最後用js來提交submit(註冊效果):
1 bindSubmitBtn: function () { 2 $("input[name='btnSubmit']").on("click", function () { 3 4 var _btn = $(this); 5 _btn.addClass("hide"); 6 var _msg = $("#msgbox"); 7 _msg.html("提交中,請稍後..."); 8 9 var _form = $("form[name='form_submit']"); 10 if (_form.valid()) { 11 _form.submit(); 12 } else { 13 _btn.removeClass("hide"); 14 _msg.html(""); 15 } 16 }); 17 }
登錄模塊
從代碼上來說登錄和註冊相差不大,功能上登錄模塊主要用來驗證登陸用戶是否存在,分配唯一sessionid,如果有跳轉地址還需要在登陸成功後執行跳回原訪問地址;
Login的Get方式Action
1 // GET: Member 2 public IActionResult Login(string returnUrl = null) 3 { 4 //獲取session 5 var userInfo = HttpContext.Session.Get<MoUserInfo>(HttpContext.Session.SessionKey()); 6 if (userInfo != null) 7 { 8 if (string.IsNullOrWhiteSpace(returnUrl)) { return RedirectToAction(nameof(HomeController.Index), "Home"); } 9 else { Redirect(returnUrl); } 10 } 11 this.MsgBox(returnUrl, "returnUrl"); 12 return View(); 13 }
Login的Post登錄方式Action
1 [HttpPost] 2 [ValidateAntiForgeryToken] 3 public async Task<IActionResult> Login([Bind("UserName,UserPwd,ReturnUrl")] MoLoginUser loginUser) 4 { 5 if (ModelState.IsValid) 6 { 7 #region 驗證 8 var md5Pwd = PublicClass._Md5(loginUser.UserPwd.Trim()); 9 var userInfo = await _context.ToUserInfo.SingleOrDefaultAsync(b => 10 b.UserName.Equals(loginUser.UserName, StringComparison.CurrentCultureIgnoreCase) && 11 b.UserPwd.Equals(md5Pwd)); 12 if (userInfo == null) 13 { 14 this.MsgBox("賬號或密碼錯誤!"); 15 return View(loginUser); 16 } 17 else if (userInfo.Status == (int)EnumHelper.EmUserStatus.禁用) 18 { 19 this.MsgBox("該賬號已被禁用,或許你可以嘗試重新註冊一個賬號!"); 20 return View(loginUser); 21 } 22 #endregion 23 24 #region 更新登錄信息 25 userInfo.Ips = this.GetUserIp(); 26 userInfo.LoginTime = DateTime.Now; 27 userInfo.LevelNum += (int)EmLevelNum.登錄; 28 29 //記錄session 30 var moUserInfo = new MoUserInfo 31 { 32 Id = userInfo.Id, 33 UserName = userInfo.UserName, 34 NickName = userInfo.NickName, 35 Addr = userInfo.Addr, 36 Birthday = userInfo.Birthday, 37 38 Blog = userInfo.Blog, 39 CreateTime = userInfo.CreateTime, 40 Email = userInfo.Email, 41 HeadPhoto = userInfo.HeadPhoto, 42 Introduce = userInfo.Introduce, 43 44 Ips = userInfo.Ips, 45 LevelNum = userInfo.LevelNum, 46 Sex = userInfo.Sex, 47 Tel = userInfo.Tel, 48 Status = userInfo.Status, 49 50 LoginTime = Convert.ToDateTime(userInfo.LoginTime) 51 }; 52 HttpContext.Session.Set<MoUserInfo>(HttpContext.Session.SessionKey(), moUserInfo); 53 54 if (!string.IsNullOrWhiteSpace(moUserInfo.Ips)) 55 { 56 _context.ToUserLog.Add(new ToUserLog 57 { 58 CodeId = (int)EmLogCode.登錄, 59 CreateTime = DateTime.Now, 60 Des = $"IP:{moUserInfo.Ips},登錄時間:{moUserInfo.LoginTime.ToString("yyyy-MM-dd HH:mm")}", 61 UserId = userInfo.Id 62 }); 63 } 64 65 _context.ToUserLog.Add(new ToUserLog 66 { 67 CodeId = (int)EmLogCode.積分, 68 CreateTime = DateTime.Now, 69 Des = $"【登錄】 +{(int)EmLevelNum.登錄}", 70 UserId = userInfo.Id 71 }); 72 73 await _context.SaveChangesAsync(); 74 75 if (string.IsNullOrWhiteSpace(loginUser.ReturnUrl)) { return RedirectToAction(nameof(HomeController.Index), "Home"); } 76 else { return Redirect(loginUser.ReturnUrl); } 77 #endregion 78 } 79 return View(loginUser); 80 }
這裡模仿微軟官方實例給出的樣子,把登陸和註冊實體模型分開了,因為登陸模型不需要什麼重覆密碼,並且還有其他的屬性如:回調地址,驗證碼等:
1 /// <summary> 2 /// 登錄實體 3 /// </summary> 4 public class MoLoginUser 5 { 6 [Required(AllowEmptyStrings = false, ErrorMessage = "賬號長度範圍6-30字元!")] 7 [Display(Prompt = "郵箱/手機號/6-30字元")] 8 [RegularExpression(@"[^\s]{6,30}", ErrorMessage = "賬號長度範圍6-30字元。")] 9 public string UserName { get; set; } 10 11 [Required(AllowEmptyStrings = false, ErrorMessage = "密碼長度範圍6-20字元!")] 12 [DataType(DataType.Password)] 13 [Display(Prompt = "密碼長度範圍6-20字元!")] 14 [RegularExpression(@"[^\s]{6,20}", ErrorMessage = "密碼長度範圍6-20字元。")] 15 public string UserPwd { get; set; } 16 17 /// <summary> 18 /// 回跳地址 19 /// </summary> 20 public string ReturnUrl { get; set; } 21 }
同理對於登錄的view設計也和註冊差不多,只是不同網站對於安全設置可能會增加一些驗證碼,或其他的驗證方式而已,如下登錄View代碼:
1 @model LovePicture.Model.MoClass.MoLoginUser 2 3 @{ 4 ViewData["Title"] = "登錄"; 5 } 6 7 <h3><span class="glyphicon glyphicon-send" aria-hidden="true"></span> 登錄</h3> 8 <form name="form_submit" asp-action="Login"> 9 <div class="form-horizontal"> 10 <h4>愛留圖:即刻登錄,讓我們一起留存珍惜的圖片吧。</h4> 11 <hr /> 12 <div asp-validation-summary="ModelOnly" class="text-danger"></div> 13 <div class="form-group"> 14 <label asp-for="UserName" class="col-md-2 control-label">賬號</label> 15 <div class="col-md-10"> 16 <input asp-for="UserName" required="required" class="form-control" /> 17 <span asp-validation-for="UserName" class="text-danger"></span> 18 </div> 19 </div> 20 <div class="form-group"> 21 <label asp-for="UserPwd" class="col-md-2 control-label">密碼</label> 22 <div class="col-md-10"> 23 <input asp-for="UserPwd" required="required" class="form-control" /> 24 <span asp-validation-for="UserPwd" class="text-danger"></span> 25 </div> 26 </div> 27 28 <div class="form-group"> 29 <div class="col-md-offset-2 col-md-10"> 30 <input type="button" value="登 錄" name="btnSubmit" class="btn btn-default" /> <a href="/member/register" title="沒賬號來這裡註冊吧">沒賬號這裡註冊</a> 31 <span id="msgbox" style="color:red">@ViewData["msgbox"]</span> 32 </div> 33 </div> 34 </div> 35 <input type="hidden" name="ReturnUrl" value="@ViewData["returnUrl"]" /> 36 </form> 37 <br /> 38 <div> 39 <a href="/member/register">沒賬號這裡註冊</a> | <a href="/member/ForgetPassword">忘記密碼?</a> 40 </div> 41 <script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script> 42 <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>View Code
郵箱找回密碼
來到這裡才真正進入今天的主題,對於一些有安全設置的網站來說通常有類似於通過綁定郵箱找回密碼的功能,這裡愛留圖目前也做了這樣的設置:
如果是綁