iT邦幫忙

2022 iThome 鐵人賽

DAY 9
1
Software Development

C# ASP.NET MVC實作: 30天打造屬於你的網站應用程式系列 第 9

(DAY 9)C# ASP .NET MVC實作: 30天打造屬於你的網站應用程式-資料驗證

  • 分享至 

  • xImage
  •  

前一天我們練習了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.ComponentModelSystem.ComponentModel.DataAnnotations這兩個命名空間,另外在View畫面會利用@Html.ValidationMessageFor()方法來顯示錯誤訊息,接著用下面範例來說明。

● 範例

我們利用DAY 8最後註冊頁面的範例來修改一下內容,步驟說明如下:

  1. 修改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; }
    }
  1. 新增控制器名為ValidateController,加入SignUp()動作方法。
    public class ValidateController : Controller
    {
        // GET: Validate

        public ActionResult SignUp()
        {
            return View();
        }
    }
  1. 新增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>
}

執行畫面如下:

  1. 新增SignUp()的POST方法,當驗證失敗時會回傳原本的頁面與顯示錯誤訊息;成功時則導向到結果呈現頁面,同時利用TempData將表單資料可以帶入指定轉向後的頁面。
        [HttpPost]
        public ActionResult SignUp(Member member)
        {
            if (!ModelState.IsValid)
            {
                return View(member);
            }
            else
            {
                TempData["Member"] = member;
                return RedirectToAction("Result");
            }
        }
  1. 新增Result()動作方法,這邊需要指定TempData的型別為Member,才能成功作為View使用的模型資料。

        public ActionResult Result()
        {
            var model = TempData["Member"] as Member;
            return View(model);
        }
  1. 最後是Result View的呈現,這邊利用HTMLHelper裡的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>
  1. 完成後實測看看結果,當欄位沒有輸入內容時按下「註冊」會顯示錯誤訊息;如果欄位沒有依照規範填寫也會報錯。

● CSRF驗證

假如View是利用範本建立的話,在form表單上方會看到一行@Html.AntiForgeryToken() 的方法,這個方法主要是用來防止CSRF (Cross Site Request Forgery),中文稱作「跨網站偽造要求」。

詳細可以參考官方說明:
https://docs.microsoft.com/zh-tw/aspnet/mvc/overview/security/xsrfcsrf-prevention-in-aspnet-mvc-and-web-pages?source=recommendations

或者版友的技術文章:
https://ithelp.ithome.com.tw/articles/10248847

● 小結

資料驗證的部分就介紹到這邊,接下來要進入另一個重頭戲:資料庫 的操作使用啦~
我們明天見!

※小弟不才,在軟體的世界還只是個小菜雞,如果內容有任何謬誤或問題,還請各位大神前輩們多多批評指教~歡迎下方留言討論^^


上一篇
(DAY 8)C# ASP .NET MVC實作: 30天打造屬於你的網站應用程式-HTML Helper
下一篇
(DAY 10)C# ASP .NET MVC實作: 30天打造屬於你的網站應用程式-使用 Entity Framework 存取與操作資料庫
系列文
C# ASP.NET MVC實作: 30天打造屬於你的網站應用程式30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言