[ASP.NET MVC] 在Model中加入資料驗證
新增驗證
開啟Movie.cs檔,看到最上方中引用了System.ComponentModel.DataAnnotations。
DataAnnotations提供內建的資料驗證用於Class與屬性中,也提供資料格式設定,如DataType能夠定義資料的格式。
接著,在Movie Class加入內建的資料驗證,修改Movie Class如下:
public class Movie
{
public int ID { get; set; }
[StringLength(60,MinimumLength=3)]
public string Title { get; set; }
[Display(Name = "日期")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime ReleaseDate { get; set; }
[Required]
[StringLength(30)]
public string Genre { get; set; }
[Range(1, 1000)]
[DataType(DataType.Currency)]
public decimal Price { get; set; }
[Required]
[StringLength(5)]
public string Rating { get; set; }
}
StringLength屬性設定字串的最大長度(MinimumLength則是最小長度)。
在伺服器總管的Movies資料表點右鍵>選擇開啟資料表定義。
開啟的資料表定義:
在上面的資料表定義可以看到所有的字串都還是設為NVARCHAR (MAX)。
接著,開啟套件管理器主控台,在視窗中輸入下列指令來更新架構:
add-migration DataAnnotations
update-database
指令執行完畢後,Visual Studio會開啟一個名為日期_DataAnnotations的Class檔案,檔案中定義了新的DbMigration以及更新資料架構的Up方法 。
public override void Up()
{
AlterColumn("dbo.Movies", "Title", c => c.String(maxLength: 60));
AlterColumn("dbo.Movies", "Genre", c => c.String(nullable: false, maxLength: 30));
AlterColumn("dbo.Movies", "Rating", c => c.String(nullable: false, maxLength: 5));
}
Genre欄位將不是nullable(必須輸入一個值),Rating的最大長度為5,Title的最大長度為60。
而Title的最小長度為3與Price的範圍並沒有產生架構的變更。
檢視Movie的資料表定義:
由資料表定義中可以看到新的長度限制,以及Genre和Rating欄位的Allow Nulls取消勾選了。
資料屬性驗證可以強制Model屬性的應用。
例如:
Required:表示該屬性為必要的,不能為空。
MinimumLength:則表示最小長度為3 。
Range:限制該值的範圍。
StringLength:用於設定字串的最大長度與最小長度。
Code First會在應用程式保存變更到資料庫之前,執行Model Class的驗證規則,驗證資料格式是否正確。
例如,在Controller的POST方法中的 db.SaveChanges() 會在資料缺失時拋出錯誤訊息:
藉由.NET Framework自動強制驗證規則的幫助,讓你的應用程式更加全面。也確保不會忘記驗證而讓一些錯誤的資料進到資料庫裡。
ASP.NET MVC的驗證介面
執行應用程式,並瀏覽到/Movies頁面。
點擊Create New連結,並新增一個新的電影。在表格中輸入一些無效的值,只要jQuery的客戶端驗證偵測到錯誤,便會在頁面上顯示錯誤訊息。
可以看到在欄位中有無效值的時候,會在欄位下方顯示錯誤訊息。錯誤訊息執行於客戶端(使用JavaScript與jQuery)與伺服器端(當使用者關閉JavaScript時)。
Controller與View會經自動提取在Movie Model Class裡的屬性驗證。這樣便不用為了驗證資料,而分別修改MoviesController Class或是Create.cshtml。
當資料有錯誤時,表格中的資料不會發送到伺服器,直到客戶端沒有驗證錯誤為止。
試著編輯電影,可以看到相同的資料驗證。
資料驗證的運作
在沒有更新Controller與View的程式碼的情況下,是如何產生資料驗證的呢?
MovieController Class的Creat方法與建立時一樣,沒有任何改變:
public ActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
if (ModelState.IsValid)
{
db.Movies.Add(movie);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}
第一個 (HTTP GET) Create 方法顯示初始的Create表格,第二個([HttpPost])Create方法處理表格的POST動作。
第二個Create方法([HttpPost])呼叫ModelState.IsValid來檢查輸入的電影資料是否有驗證上的錯誤,如果沒有驗證錯誤才會將新的電影儲存到資料庫中。
在客戶端驗證錯誤時,資料不會POST到伺服器端呼叫HttpPost的Create方法。
如果在瀏覽器關閉JavaScript,那麼客戶端的驗證將會關閉。資料會POST到伺服器端呼叫HttpPost的Create方法,ModelState.IsValid則會在這個時候驗證資料是否有錯誤。
在HttpPost Create設中斷點確認方法不會被呼叫到。接著關閉瀏覽器中的JavaScript,那麼錯誤的資料便會遞交到伺服器端,並執行到Create方法的中斷點。
不過雖然錯誤的資料被傳送到伺服器端,但是仍然能有ModelState.IsValid的資料驗證。
在IE關閉JavaScript:
在Chrome關閉JavaScript:
若關閉JavaScript便會執行HttpPost的Create方法:
在Create.cshtml 中,會依照Create方法而顯示初始的表單或是在有驗證錯誤的時候顯示錯誤訊息。
程式碼中使用Html.EditorFor輸出每個電影屬性的<input>元素。 而Html.ValidationMessageFor則會在資料驗證有錯誤時顯示錯誤訊息。
這兩個HtmlHelper方法作用於Controller傳送到View的Movie Model,並自動檢視Model的驗證屬性來適當的顯示錯誤訊息。
不論是Controller或是View都能透過Movie Class的規則進行驗證與顯示相對應的錯誤訊息。Movie Class的驗證規則也可以應用在編輯(Edit )的View以及其它會新增或是修改Model的View。
因此,要是想修改驗證規則只需要修改Movie的Model就好,不用再修改其他地方。所有的驗證邏輯都會被定義在Model中,並應用在任何地方。
這能夠讓程式碼變得乾淨簡單,更容易維護。
DataType 屬性
開啟Movie.cs檔並檢視Movie Class,頂端的命名空間System.ComponentModel.DataAnnotations除了提供驗證屬性也提供格式的屬性。
在先前已經對於日期與價格(Price)做DataType的應用,如下面的程式碼:
[Display(Name = "日期")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime ReleaseDate { get; set; }
[Range(1, 1000)]
[DataType(DataType.Currency)]
public decimal Price { get; set; }
DataType屬性提供資料格式給View,除了上面的DataType.Date與DataType.Currency外,還有以下格式:
[DataType(DataType.EmailAddress)] //Mail格式
[DataType(DataType.PhoneNumber)] //電話格式
[DataType(DataType.Url)] //Url格式
DataType屬性並不會提供資料驗證。可以RegularExpression屬性來進行驗證,如下列程式碼規定只能輸入規定的文字:
[RegularExpression(@"^[A-Z]+[a-zA-Z''-'\s]*$")]
DataType屬性可以指出比資料庫更明確的資料格式,但不提供資料驗證。
在Movie Class中的日期格式中只顯示日期而不顯時間。
DataType提供多種的資料型態,如:日期、時間、電話、貨幣、郵件地址...等。
DataType還可以讓應用程式自動提供特定類型的功能,例如要產生一個郵件連結可以用DataType.EmailAddress,日期選擇則可以用DataType.Date來表達HTML5的type="date"。
DataType.Date並沒有指定日期的格式,可以使用DisplayFormat來指定日期的格式:
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime EnrollmentDate { get; set; }