[ASP.NET MVC] 前台會員修改個人資料範例教學 #CH3
在學習 C# 與資料庫的互動方式,有一個常見且實用的教學就是會員登入、註冊與修改會員資料等範例,學習過程中會用到資料庫新增、修改與查詢動作,是理解程式與資料庫互動的常見程式碼。
當學會了這個範例,將來為客戶開發系統的時候,馬上可以派上用場。
此篇文章是繼上一篇文章: 前台會員登入範例 #CH2 接續教學。
範例內容主要以 ASP.NET MVC 為核心,前端使用 Vue.js 框架,而後端使用 SQL Server 當資料庫。
Vue.js 是前端3 大主流框架的其中之一,目標是透過簡單的 API 提供開發者實作資料綁定與操作網頁上的元件,Vue.js 的核心把焦點關注在狀態與畫面的同步層級上,適合與其他 JavsScript 函式庫整合,同時也適合當作 ASP.NET MVC 的前端框架。
SQL Server 是微軟推出的關聯式資料庫,使用 SQL 語言就可以輕鬆操作資料庫。
編寫此教學文章是為了幫助更多新加入的軟體工程師們,有更簡單實用的範例,可以快速學習程式語言。
這次我將會簡化這個基礎必學的前端會員範例,適合剛接觸 C# 與資料庫程式的新手學習。
文末有提供此操作範例的完整程式碼下載,有需要可以自行下載瀏覽。
Contents
在 MemberController 增加修改個人資料頁面
在上一篇已經完成了 MemberController.cs 的建立。
這裡要新增一個修改個人資料的畫面,在 MemberController 類別內,增加 EditProfile() 是呈現畫面的 Action。
1 2 3 4 5 |
// GET: 修改個人資料頁面 public ActionResult EditProfile() { return View(); } |
增加修改個人資料頁面 View
在 EditProfile()
語法上按右鍵選「新增檢視」。
選擇「MVC 5 檢視」加入。
確認名稱為 “EditProfile”,有勾選「使用版面配置頁」。
新增之後在 Views\Member\EditProfile.cshtml 會新增 View 檢視頁面。
編寫修改個人資料 View 語法
在 Bootstrap 3 的官方範例,有提供表單的範例、面版的範例及按鈕的範例。
我從 Bootstrap 3 範例中語法組合變成我的修改個人資料畫面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
<!--使用 Bootstrap 設計登入表單--> <div class="panel panel-primary"> <div class="panel-heading">修改個人資料範例</div> <div class="panel-body"> <div class="form-group"> <label>帳號</label> <p class="form-control-static"></p> </div> <div class="form-group"> <label>姓名</label> <input type="text" class="form-control"> </div> <div class="form-group"> <label>Email</label> <input type="text" class="form-control"> </div> </div> <div class="panel-footer"> <button type="button" class="btn btn-primary">修改個人資料</button> </div> </div> <!--使用 Bootstrap 設計登入表單--> <div class="panel panel-primary"> <div class="panel-heading">修改密碼範例</div> <div class="panel-body"> <div class="row"> <div class="col-md-6"> <div class="form-group"> <label>修改密碼</label> <input type="password" class="form-control"> </div> </div> <div class="col-md-6"> <div class="form-group"> <label>確認新密碼</label> <input type="password" class="form-control"> </div> </div> </div> </div> <div class="panel-footer"> <button type="button" class="btn btn-primary">修改密碼</button> </div> </div> |
在 EditProfile.cshtml 增加這些語法後,畫面就會出現修改個人資料及修改密碼表單。
這只是一個沒有功能,純畫面的表單,目的在展示 Bootstrap 的語法。
加入 Vue.js 控制元件
我們在前面 _Layout.cshtml 已經加了 Vue.js 的底層元件,所以這頁面,就可以套用 Vue.js 的寫法。
我將剛剛的 HTML 修改一下,加入了 Vue.js 語法,並增加 GetUserProfile()
,DoEditProfile()
,DoEditPwd()
3 個方法,可傳送表單到 Controller 頁面。
我額外增加了 Bootstrap 的 modal 樣式,來顯示後端執行時的錯誤,這樣方便 Debug。
以下程式碼可以整個取代 EditProfile.cshtml 內容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
<div id="VuePage"> <!--使用 Bootstrap 設計登入表單--> <div class="panel panel-primary"> <div class="panel-heading">修改個人資料範例</div> <div class="panel-body"> <div class="form-group"> <label>帳號</label> <p class="form-control-static">{{form.UserID}}</p> </div> <div class="form-group"> <label>姓名</label> <input type="text" class="form-control" v-model="form.UserName"> </div> <div class="form-group"> <label>Email</label> <input type="text" class="form-control" v-model="form.UserEmail"> </div> </div> <div class="panel-footer"> <button type="button" class="btn btn-primary" v-on:click="DoEditProfile()">修改個人資料</button> </div> </div> <!--使用 Bootstrap 設計登入表單--> <div class="panel panel-primary"> <div class="panel-heading">修改密碼範例</div> <div class="panel-body"> <div class="row"> <div class="col-md-6"> <div class="form-group"> <label>修改密碼</label> <input type="password" class="form-control" v-model="form.NewUserPwd"> </div> </div> <div class="col-md-6"> <div class="form-group"> <label>確認新密碼</label> <input type="password" class="form-control" v-model="form.CheckUserPwd"> </div> </div> </div> </div> <div class="panel-footer"> <button type="button" class="btn btn-primary" v-on:click="DoEditPwd()">修改密碼</button> </div> </div> <!--使用 Bootstrap Modal 樣式,當執行有錯誤時,顯示錯誤訊息--> <div class="modal fade" id="ErrorAlert" tabindex="-1" role="dialog"> <div class="modal-dialog modal-lg" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> <h4 class="modal-title">錯誤訊息</h4> </div> <div class="modal-body" id="ErrorMsg" style="overflow-x:auto;width:100%;"> </div> </div><!-- /.modal-content --> </div><!-- /.modal-dialog --> </div><!-- /.modal --> </div> @section scripts { <script> var VuePage = new Vue({ el: '#VuePage' , data: function () { var data = { form: {} }; // 設定表單初始值 data.form = { UserID: "" , UserName: "" , UserEmail:"" } return data; } // Vue 實體與掛載完成 , mounted: function () { var self = this; // 當 Vue 掛載完成,取得個人資料 self.GetUserProfile(); } , methods: { // 前端驗證權杖 GetToken: function () { var token = '@Html.AntiForgeryToken()'; token = $(token).val(); return token; } // 取得個人資料 , GetUserProfile: function () { var self = this; var postData = {}; // 使用 jQuery Ajax 傳送至後端 $.ajax({ url:'@Url.Content("~/Member/GetUserProfile")', method:'POST', dataType:'json', data: { inModel: postData }, success: function (datas) { if (datas.ErrMsg) { alert(datas.ErrMsg); return; } self.form.UserID = datas.UserID; self.form.UserName = datas.UserName; self.form.UserEmail = datas.UserEmail; }, error: function (err) { $('#ErrorMsg').html(err.responseText); $('#ErrorAlert').modal('toggle'); }, }); } // 修改個人資料 , DoEditProfile: function () { var self = this; // 組合表單資料 var postData = {}; postData['UserName'] = self.form.UserName; postData['UserEmail'] = self.form.UserEmail; // 使用 jQuery Ajax 傳送至後端 $.ajax({ url:'@Url.Content("~/Member/DoEditProfile")', method:'POST', dataType:'json', data: { inModel: postData, __RequestVerificationToken: self.GetToken() }, success: function (datas) { if (datas.ErrMsg) { alert(datas.ErrMsg); return; } alert(datas.ResultMsg); }, error: function (err) { $('#ErrorMsg').html(err.responseText); $('#ErrorAlert').modal('toggle'); }, }); } // 修改密碼 , DoEditPwd: function () { var self = this; // 組合表單資料 var postData = {}; postData['NewUserPwd'] = self.form.NewUserPwd; postData['CheckUserPwd'] = self.form.CheckUserPwd; // 使用 jQuery Ajax 傳送至後端 $.ajax({ url:'@Url.Content("~/Member/DoEditPwd")', method:'POST', dataType:'json', data: { inModel: postData, __RequestVerificationToken: self.GetToken() }, success: function (datas) { if (datas.ErrMsg) { alert(datas.ErrMsg); return; } alert(datas.ResultMsg); }, error: function (err) { $('#ErrorMsg').html(err.responseText); $('#ErrorAlert').modal('toggle'); }, }); } } }) </script> } |
我使用 Vue.js 生命週期中的 mounted 事件,當 Vue 掛載完成,就呼叫 GetUserProfile()
方法取得個人資料,取得後端會員資料後,再放到前端畫面上。
當使用者修改資料時,就呼叫 DoEditProfile()
或 DoEditPwd()
方法,將前端資料往後端送。
關於 Vue.js 的教學語法,可以到官網上面查詢,官網有完整的教學。
編寫Controller 語法
取得個人資料 Controller 語法
剛剛 View 在頁面載入完成時會呼叫 ~/Member/GetUserProfile
方法,以下是 GetUserProfile()
的 Controller 寫法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
/// <summary> /// 取得個人資料 /// </summary> /// <returns></returns> public ActionResult GetUserProfile() { GetUserProfileOut outModel = new GetUserProfileOut(); // 檢查會員 Session 是否存在 if (Session["UserID"] == null || Session["UserID"].ToString() == "") { outModel.ErrMsg = "無會員登入記錄"; return Json(outModel); } // 取得連線字串 string connStr = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["ConnDB"].ConnectionString; // 當程式碼離開 using 區塊時,會自動關閉連接 using (SqlConnection conn = new SqlConnection(connStr)) { // 資料庫連線 conn.Open(); // 取得會員資料 string sql = "select * from Member where UserID = @UserID"; SqlCommand cmd = new SqlCommand(); cmd.CommandText = sql; cmd.Connection = conn; // 使用參數化填值 cmd.Parameters.AddWithValue("@UserID", Session["UserID"]); // 執行資料庫查詢動作 SqlDataAdapter adpt = new SqlDataAdapter(); adpt.SelectCommand = cmd; DataSet ds = new DataSet(); adpt.Fill(ds); DataTable dt = ds.Tables[0]; if (dt.Rows.Count > 0) { // 將資料回傳給前端 outModel.UserID = dt.Rows[0]["UserID"].ToString(); outModel.UserName = dt.Rows[0]["UserName"].ToString(); outModel.UserEmail = dt.Rows[0]["UserEmail"].ToString(); } else { outModel.ErrMsg = "查無會員資料"; } } // 回傳 Json 給前端 return Json(outModel); } |
這方法主要是取得 Session 中的 UserID,將 UserID 查詢資料庫 Member 表中的 UserID 欄位,將資料表中的資料,回傳到前端去。
修改個人資料 Controller 語法
當在畫面上執行「修改個人資料」按鈕,View 會呼叫 ~/Member/DoEditProfile
,以下是 DoEditProfile()
的 Controller 寫法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
/// <summary> /// 修改個人資料 /// </summary> /// <param name="inModel"></param> /// <returns></returns> [ValidateAntiForgeryToken] public ActionResult DoEditProfile(DoEditProfileIn inModel) { DoEditProfileOut outModel = new DoEditProfileOut(); // 檢查個人資料是否有輸入 if (string.IsNullOrEmpty(inModel.UserName) || string.IsNullOrEmpty(inModel.UserEmail)) { outModel.ErrMsg = "請輸入資料"; return Json(outModel); } // 檢查會員 Session 是否存在 if (Session["UserID"] == null || Session["UserID"].ToString() == "") { outModel.ErrMsg = "無會員登入記錄"; return Json(outModel); } // 取得連線字串 string connStr = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["ConnDB"].ConnectionString; // 當程式碼離開 using 區塊時,會自動關閉連接 using (SqlConnection conn = new SqlConnection(connStr)) { // 資料庫連線 conn.Open(); // 修改個人資料至資料庫 string sql = @"UPDATE Member SET UserName = @UserName, UserEmail = @UserEmail WHERE UserID = @UserID"; SqlCommand cmd = new SqlCommand(); cmd.Connection = conn; cmd.CommandText = sql; // 使用參數化填值 cmd.Parameters.AddWithValue("@UserID", Session["UserID"]); cmd.Parameters.AddWithValue("@UserName", inModel.UserName); cmd.Parameters.AddWithValue("@UserEmail", inModel.UserEmail); // 執行資料庫更新動作 int Ret = cmd.ExecuteNonQuery(); if (Ret > 0) { outModel.ResultMsg = "修改個人資料完成"; } else { outModel.ErrMsg = "無異動資料"; } } // 回傳 Json 給前端 return Json(outModel); } |
這次方法特別在開頭加一個 [ValidateAntiForgeryToken] 驗證,這是防止跨網站偽造要求的攻擊,也稱為 CSRF (Cross-Site Request Forgery) 攻擊,這是對 MVC 網頁提升安全性的做法。
在前端的 DoEditProfile()
方法內在 Ajax 傳送資料時,需要加一段 __RequestVerificationToken: self.GetToken()
參數,將前端驗證碼往後端傳送,後端才能驗證來源是合法來源。
修改資料時的 SQL 都是使用參數化填值方式,這是為了防止 SQL 注入攻擊。
此功能是先經過登入才能呈現的畫面,在登入成功時,已經帳號存入 Session 內,在此頁面帳號由 Session 來取得,就不需要由前端傳送了。
修改密碼 Controller 語法
當在畫面上執行「修改密碼」按鈕,View 會呼叫 ~/Member/DoEditPwd
,以下是 DoEditPwd()
的 Controller 寫法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
/// <summary> /// 修改密碼 /// </summary> /// <param name="inModel"></param> /// <returns></returns> [ValidateAntiForgeryToken] public ActionResult DoEditPwd(DoEditPwdIn inModel) { DoEditPwdOut outModel = new DoEditPwdOut(); // 檢查是否有輸入密碼 if (string.IsNullOrEmpty(inModel.NewUserPwd)) { outModel.ErrMsg = "請輸入修改密碼"; return Json(outModel); } if (string.IsNullOrEmpty(inModel.CheckUserPwd)) { outModel.ErrMsg = "請輸入確認新密碼"; return Json(outModel); } if (inModel.NewUserPwd != inModel.CheckUserPwd) { outModel.ErrMsg = "新密碼與確認新密碼不相同"; return Json(outModel); } // 檢查會員 Session 是否存在 if (Session["UserID"] == null || Session["UserID"].ToString() == "") { outModel.ErrMsg = "無會員登入記錄"; return Json(outModel); } // 將新密碼使用 SHA256 雜湊運算(不可逆) string salt = Session["UserID"].ToString().Substring(0, 1).ToLower(); //使用帳號前一碼當作密碼鹽 SHA256 sha256 = SHA256.Create(); byte[] bytes = Encoding.UTF8.GetBytes(salt + inModel.NewUserPwd); //將密碼鹽及新密碼組合 byte[] hash = sha256.ComputeHash(bytes); StringBuilder result = new StringBuilder(); for (int i = 0; i < hash.Length; i++) { result.Append(hash[i].ToString("X2")); } string NewPwd = result.ToString(); // 雜湊運算後密碼 // 取得連線字串 string connStr = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["ConnDB"].ConnectionString; // 當程式碼離開 using 區塊時,會自動關閉連接 using (SqlConnection conn = new SqlConnection(connStr)) { // 資料庫連線 conn.Open(); // 修改個人資料至資料庫 string sql = @"UPDATE Member SET UserPwd = @UserPwd WHERE UserID = @UserID"; SqlCommand cmd = new SqlCommand(); cmd.Connection = conn; cmd.CommandText = sql; // 使用參數化填值 cmd.Parameters.AddWithValue("@UserID", Session["UserID"]); cmd.Parameters.AddWithValue("@UserPwd", NewPwd); // 執行資料庫更新動作 int Ret = cmd.ExecuteNonQuery(); if (Ret > 0) { outModel.ResultMsg = "修改密碼完成"; } else { outModel.ErrMsg = "無異動資料"; } } // 回傳 Json 給前端 return Json(outModel); } |
修改密碼建議使用者輸入 2 次,以確保沒有手誤,同時在後端驗證 2 次密碼是否相同。
資料庫內的密碼建議經過雜湊運算後再儲存,以防止被盜取密碼時,使用者重要密碼外洩。
此功能是先經過登入才能呈現的畫面,登入帳號由 Session 來取得,就不需要由前端傳送了,修改資料時的會員帳號也是由 Session 內的值提供。
增加修改個人資料 Model
剛剛在 Controller 建立時,新增了一些新參數類別及新回傳類別,打開 MemberModel.cs 檔案,在裡面增加新增的類別。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
/// <summary> /// 取得個人資料回傳 /// </summary> public class GetUserProfileOut { public string ErrMsg { get; set; } public string UserID { get; set; } public string UserName { get; set; } public string UserEmail { get; set; } } /// <summary> /// 修改個人資料參數 /// </summary> public class DoEditProfileIn { public string UserName { get; set; } public string UserEmail { get; set; } } /// <summary> /// 修改個人資料回傳 /// </summary> public class DoEditProfileOut { public string ErrMsg { get; set; } public string ResultMsg { get; set; } } /// <summary> /// 修改密碼參數 /// </summary> public class DoEditPwdIn { public string NewUserPwd { get; set; } public string CheckUserPwd { get; set; } } /// <summary> /// 修改密碼回傳 /// </summary> public class DoEditPwdOut { public string ErrMsg { get; set; } public string ResultMsg { get; set; } } |
關於 Controller 與 View 之間的資料傳遞物件都定義在 Model 裡面
測試修改個人資料功能
之前做登入的時候,登入成功只是 alert 訊息而已,這次在 alert 之後,增加導向到「修改個人資料」頁面。
打開 \Views\Member\Login.cshtml 頁面,在 DoLogin() 方法內增加以下語法。
window.location = '@Url.Content("~/Member/EditProfile")';
在 VS 按 <F5> 執行專案,先在「登入」畫面,輸入正確的帳號密碼,就會跳到「修改個人資料畫面」。
在顯示「修改個人資料」頁面會先顯示會員資料。
接著可以分別測試「修改個人資料」與「修改密碼」兩個功能。
重點整理
- 在 Controller 增加 EditProfile() 顯示修改個人資料畫面
- 使用 Bootstrap 樣式可快速製作美觀的表單
- 在 View 增加取得個人資料、修改個人資料及修改密碼的方法
- 在 Controller 後端增加對應的 3 個方法
- Model 定義 Controller 與 View 之間的資料傳遞物件
範例下載
此範例包含完整範例內容,連結 GitHub 下載範例
下一篇教學文章
相關學習文章
如果你在學習上有不懂的地方,需要諮詢服務,可以參考站長服務,我想辨法解決你的問題
如果文章內容有過時、不適用或錯誤的地方,幫我在下方留言通知我一下,謝謝
我從台灣 .NET 技術愛好者俱樂部過來瞧瞧~~
有幾個想法交流交流:
1. 連取得個人資料好像都依賴著前端ajax,感覺C#沒什麼大用途了XD…這邊我會想要在後端Action就return model回去,可能是為了展示Vue?
2. 可以用你的Code好好研究Vue一下~因為寫得很清楚
3. Action部分,是不是補上Method會好一點?
4. url部分,因為我曾經吃過虧,看到波浪就怕,是不是使用@Url.Action()會比較好呢?
5. 取token部分我還沒想過這樣取,挺酷,我通常是把token放在layout,然後統一用jquery去取__RequestVerificationToken的值
我回答一些我的想法
1. 從前端送 Ajax 到後端取個人資料,是覺得畫面比較快載入,資料可以後補上,假如取資料的速度慢,也不會影響前端等待太久的體驗。
2. 謝謝
3. Action部分,是不是補上Method會好一點,是指說將共用的方法放在 Method 然後在呼叫 Method 就行嗎?
因為我在寫範例,所以改成全部放在一起看,我實務上的專案,相同方法會抽離出來的。
4. 你要改成 @Url.Action() 也行,但遇到想再增加 ?Param=value 會怎麼加呢?
我用 @Url.Content 是覺得比較靈活一點
5. 取 token部分我也是因為範例,如果拉到各頁面展示,以實務開發,也是建議放在 layout 裡面共用
謝謝你的交流
3 哦抱歉我說的太含糊~~我指的是在Action前面加上 [HttpPost] 或者 [HttpGet] 標籤,算是MVC在用的
4. 參數的用法,
@Url.Action(“ActionName”,”ControllerName”, new {Param=value});
不過意思的確是一樣,你用Content感覺會比較像前端串參數的用法。
3. [HttpPost] 或者 [HttpGet] 這個我知道,但我不寫的原因是因為兩者都呼叫的到,所以就不加了,我是希望程式碼愈少愈好
4. 看起來兩個方式都可以達到同樣的結果, @Url.Action(“ActionName”,”ControllerName”, new {Param=value}); 的方式會更適合 MVC 使用,這個我思考一下未來要不要替換成這個,謝啦