前一天我們練習了HTML Helper的使用方式,並且建立了一個簡單的註冊功能頁面,不過思考一下還有很多地方可以改進,其中之一就是關於資料驗證的部分。譬如表單內的欄位必填、帳號密碼格式是否符合要求、欄位只能填數字...等等,我們必須要確保使用者在填寫表單內容時是照預期地填入,避免後續處理資料麻煩。
表單資料驗證主要可以分為「前端資料驗證」與「後端資料驗證」,前端是指在使用者按下Submit按鈕後,送出表單前就先檢查錯誤並提示;後端則是將資料送至伺服器端驗證進行判斷再回傳錯誤訊息。
前端最簡單的表單驗證方式,就是直接靠HTML5針對<input>
標籤提供的屬性功能,例如下列的Code:
<div>
<form method="post">
<input type="email" required="required" placeholder="請輸入電子信箱格式" />
<input type="submit" class="btn btn-primary" />
</form>
</div>
type="email"
代表必須輸入email格式,否則會提示錯誤。placeholder
屬性可以自訂給使用者填寫的說明;required
屬性表示必填,不填送出一樣會提示。
另外也有將欄位設計成特定樣式,像type="number"
會多了上下調整數字按鈕,並限制只能輸入數字;type="range"
使用滑桿調整數值;type="date"
可以直接選取日期清單,有興趣可以自行玩玩,這邊不多做示範。
如果想要讓使用者輸入特定格式內容,例如只能輸入英文大小寫、符合身分證格式等等,則可以使用pattern
屬性搭配 正規表達式(Regular Expression)來達成目的。例如下列Code寫法代表欄位必須輸入3個大寫或小寫英文,否則會出現錯誤提示。
<div>
<form method="post">
<input type="text" required="required" placeholder="請輸入連續3個英文大小寫" pattern="[a-zA-z]{3}" />
<br />
<input type="submit" class="btn btn-primary" />
</form>
</div>
下列Code寫法則代表欄位必須輸入身分證格式(必須是大寫字母開頭/第2位必須是數字1或2/第3位開始到結尾為8個數字)。
※身分證編碼的更詳細規範可以參考這篇文章:
https://returnbool.pixnet.net/blog/post/10268673
<div>
<form method="post">
<input type="text" required="required" placeholder="請輸入身分證字號" pattern="^[A-Z]{1}[1-2]{1}[0-9]{8}$" />
<br />
<input type="submit" class="btn btn-primary" />
</form>
</div>
正規表達式的規則非常多樣化,也可以自由搭配,詳細可以參考維基百科說明:
https://zh.wikipedia.org/zh-tw/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F#PCRE%E8%A1%A8%E8%BE%BE%E5%BC%8F%E5%85%A8%E9%9B%86
以上所述的前端驗證方式其實仍有可以改善之處,例如:當不符合正規表達式時可以自訂詳細錯誤內容、錯誤欄位外框可以反紅醒目提示、檢查身分證數字是否符合規則...等等,這些可以利用JavaScript語法技巧來完成,但因為30天主題還是聚焦在ASP .NET MVC的流程,這邊就沒辦法詳細來說明了@_@
後端驗證要講的是關於ASP .NET MVC所提供的模型驗證(Model Validate)方式,我們可以在資料Model的每個屬性上面增加驗證用的屬性,與使用ErrorMessage
自訂錯誤訊息。附加的驗證屬性需搭配在Controller的動作方法內,並搭配條件ModelState.IsValid
來判斷是否資料有通過驗證。
常用的附加驗證屬性如下:
附加屬性名稱 | 用途 | 範例 |
---|---|---|
[DisplayName] | 設定欄位顯示的名稱 | [DisplayName("帳號")] |
[Required] | 設定欄位為必填 | [Required] |
[Range] | 設定欄位內容數值範圍 | [Range(1, 100, ErrorMessage = "年齡須在1~100之間")] |
[Compare] | 與指定的欄位比較內容是否相同 | [Compare("Password", ErrorMessage = "兩組密碼必須相同")] |
[EmailAddress] | 驗證欄位是否為Email格式 | [EmailAddress] |
[Url] | 驗證欄位是否為網址格式 | [Url] |
[StringLength] | 設定欄位字串長度 | [StringLength(12,MinimumLength = 6)] |
[RegularExpression] | 設定欄位內容必須符合自訂的正規表達式 | [RegularExpression("[a-zA-z]{3}")] |
要能使用驗證屬性必須先引用 System.ComponentModel
與 System.ComponentModel.DataAnnotations
這兩個命名空間,另外在View畫面會利用@Html.ValidationMessageFor()
方法來顯示錯誤訊息,接著用下面範例來說明。
我們利用DAY 8最後註冊頁面的範例來修改一下內容,步驟說明如下:
Member
類別的內容如下方Code: public class Member
{
[DisplayName("帳號")]
[Required(ErrorMessage ="帳號名稱不可空白")]
public string Id { get; set; }
[DisplayName("密碼")]
[Required]
public string Password { get; set; }
[DisplayName("再次輸入密碼")]
[Required]
[Compare("Password",ErrorMessage ="輸入密碼必須相同")]
public string Password2 { get; set; }
[DisplayName("年齡")]
[Required]
[Range(0,100,ErrorMessage ="年齡必須在0-100之間")]
public int Age { get; set; }
[DisplayName("生日")]
[Required]
public DateTime Birthday { get; set; }
[DisplayName("電子信箱")]
[Required]
[EmailAddress(ErrorMessage ="Email格式有誤")]
public string Email { get; set; }
}
ValidateController
,加入SignUp()
動作方法。 public class ValidateController : Controller
{
// GET: Validate
public ActionResult SignUp()
{
return View();
}
}
Sign()
方法的View頁面。@using HTMLHelperDemo.Models
@model Member
@{
ViewBag.Title = "SignUp";
}
<h2>SignUp</h2>
@using (Html.BeginForm("SignUp", "Validate"))
{
<div class="container">
<div class="form-group">
@Html.LabelFor(m => m.Id)
@Html.TextBoxFor(m => m.Id, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Id, "", new { @class = "text-danger" })
</div>
<div class="form-group">
@Html.LabelFor(m => m.Password)
@Html.PasswordFor(m => m.Password, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" })
</div>
<div class="form-group">
@Html.LabelFor(m => m.Password2)
@Html.PasswordFor(m => m.Password2, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Password2, "", new { @class = "text-danger" })
</div>
<div class="form-group">
@Html.LabelFor(m => m.Age)
@Html.TextBoxFor(m => m.Age, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Age, "", new { @class = "text-danger" })
</div>
<div class="form-group">
@Html.LabelFor(m => m.Email)
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Email, "", new { @class = "text-danger" })
</div>
<div class="form-group">
@Html.LabelFor(m => m.Birthday)
@Html.TextBoxFor(m => m.Birthday, new { @class = "form-control", type = "date" })
@Html.ValidationMessageFor(m => m.Birthday, "", new { @class = "text-danger" })
</div>
<div class="form-group">
<input type="submit" value="註冊" class="btn btn-primary" />
</div>
</div>
}
執行畫面如下:
SignUp()
的POST方法,當驗證失敗時會回傳原本的頁面與顯示錯誤訊息;成功時則導向到結果呈現頁面,同時利用TempData
將表單資料可以帶入指定轉向後的頁面。 [HttpPost]
public ActionResult SignUp(Member member)
{
if (!ModelState.IsValid)
{
return View(member);
}
else
{
TempData["Member"] = member;
return RedirectToAction("Result");
}
}
Result()
動作方法,這邊需要指定TempData
的型別為Member
,才能成功作為View使用的模型資料。
public ActionResult Result()
{
var model = TempData["Member"] as Member;
return View(model);
}
DisplayNameFor()
與 DisplayFor()
簡單呈現註冊成功後的資料,因為在Member
類別內有指定DisplayName
Attribute,所以顯示的不再是類別屬性名稱,而是指定的字串了。@model HTMLHelperDemo.Models.Member
@{
ViewBag.Title = "Result";
}
<h2>Result</h2>
<div class="container">
<ul>
<li>@Html.DisplayNameFor(m => m.Id) : @Html.DisplayFor(m => m.Id)</li>
<li>@Html.DisplayNameFor(m => m.Password) : @Html.DisplayFor(m => m.Password)</li>
<li>@Html.DisplayNameFor(m => m.Password2) : @Html.DisplayFor(m => m.Password2)</li>
<li>@Html.DisplayNameFor(m => m.Age) : @Html.DisplayFor(m => m.Age)</li>
<li>@Html.DisplayNameFor(m => m.Email) : @Html.DisplayFor(m => m.Email)</li>
<li>@Html.DisplayNameFor(m => m.Birthday) : @Html.DisplayFor(m => m.Birthday)</li>
</ul>
</div>
假如View是利用範本建立的話,在form表單上方會看到一行@Html.AntiForgeryToken()
的方法,這個方法主要是用來防止CSRF (Cross Site Request Forgery),中文稱作「跨網站偽造要求」。
或者版友的技術文章:
https://ithelp.ithome.com.tw/articles/10248847
資料驗證的部分就介紹到這邊,接下來要進入另一個重頭戲:資料庫 的操作使用啦~
我們明天見!
※小弟不才,在軟體的世界還只是個小菜雞,如果內容有任何謬誤或問題,還請各位大神前輩們多多批評指教~歡迎下方留言討論^^