.NET Magazine國際中文電子雜誌
作 者:許薰尹
審 稿:張智凱
文章編號:N200822102
出刊日期: 2020/8/19
這篇文章將延續《Razor Page入門 - 5》一文的情境,介紹當在ASP.NET Core Razor Page網站應用程式中新增資料的作業。
設計資料新增功能
先修改「MyServices」服務專案「IBookRepository」介面程式碼,新增一個「Create」方法,根據「editBook」參數「newBook」的值,來修新增書資料,並回傳修改過的「Book」物件。我們希望「Book.Id」自動編流水號,新的編號將會放在回傳的「Book」物件「Id」屬性中:
MyServices\IBookRepository.cs
using MyModels;
using System;
using System.Collections.Generic;
using System.Text;
namespace MyServices {
public interface IBookRepository {
IEnumerable<Book> GetAllBooks();
Book GetBook( int id );
Book Update( Book editBook );
Book Create( Book newBook );
}
}
接著我們在「BookRepository」類別中,實作「Create」方法,在方法中計算出流水號,請參考以下程式碼:
MyServices\BookRepository.cs
using MyModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyServices {
public class BookRepository : IBookRepository {
private List<Book> _books = null;
public BookRepository() {
_books = new List<Book>() {
new Book() {
Id = 1 ,
Title = " Essential Programming Language " ,
Price = 250 ,
PublishDate = new DateTime( 2019 ,1,2 ) ,
InStock = true ,
Description = "Essential Programming Language " ,
Category = Category.Computers
},
new Book() {
Id = 2 ,
Title = " Telling Arts " ,
Price = 245 ,
PublishDate = new DateTime( 2019 , 4 , 15 ) ,
InStock = true ,
Description = " Telling Arts " ,
Category = Category.Arts
},
new Book() {
Id = 3 ,
Title = " Marvel " ,
Price = 150 ,
PublishDate = new DateTime( 2019 , 2, 21 ) ,
InStock = true ,
Description = " Marvel " ,
Category = Category.Commics
},
new Book() {
Id = 4 ,
Title = " The Beauty of Cook" ,
Price = 450 ,
PublishDate = new DateTime( 2019 ,12,2 ) ,
InStock = true ,
Description = " The Beauty of Cook " ,
Category = Category.Cooking
}
};
}
public Book Create( Book newBook ) {
newBook.Id = _books.Max( b => b.Id ) + 1;
_books.Add( newBook );
return newBook;
}
public IEnumerable<Book> GetAllBooks() {
return _books;
}
public Book GetBook( int id ) {
return _books.FirstOrDefault( b => b.Id == id );
}
public Book Update( Book editBook ) {
Book book = _books.FirstOrDefault( b => b.Id == editBook.Id );
if ( book!=null ) {
book.Title = editBook.Title;
book.Price = editBook.Price;
book.PublishDate = editBook.PublishDate;
book.InStock = editBook.InStock;
book.Description = editBook.Description;
book.Category = editBook.Category;
}
return book;
}
}
}
修改「_Layout.cshtml」檔案,加入一個選單選項,利用「Create」超連結連接到「Create」Razor Page,參考以下程式碼:
MyRazorWeb\Pages\Shared\_Layout.cshtml
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset = "utf-8" />
<meta name = "viewport" content = "width=device-width, initial-scale = 1.0" />
<title> @ViewData["Title"] – MyRazorWeb </title>
<link rel = "stylesheet" href = "~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel = "stylesheet" href = "~/css/site.css" />
</head>
<body>
<header>
<nav class = "navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class = "container">
<a class = "navbar-brand" asp-area = "" asp-page = "/Index"> MyRazorWeb </a>
<button class = "navbar-toggler" type = "button" data-toggle = "collapse" data-target = ".navbar-collapse" aria-controls = "navbarSupportedContent"
aria-expanded = "false" aria-label = "Toggle navigation">
<span class = "navbar-toggler-icon"> </span>
</button>
<div class = "navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
<ul class = "navbar-nav flex-grow-1">
<li class = "nav-item">
<a class = "nav-link text-dark" asp-area = "" asp-page = "/Index"> Home </a>
</li>
<li class = "nav-item">
<a class = "nav-link text-dark" asp-area = "" asp-page = "/Books/List"> Books </a>
</li> <li class = "nav-item">
<a class = "nav-link text-dark" asp-area = "" asp-page = "/Books/Create"> Create </a>
</li>
<li class = "nav-item">
<a class = "nav-link text-dark" asp-area = "" asp-page = "/Privacy"> Privacy </a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class = "container">
<main role = "main" class = "pb-3">
@RenderBody()
</main>
</div>
<footer class = "border-top footer text-muted">
<div class = "container">
© 2020 - MyRazorWeb - <a asp-area = "" asp-page = "/Privacy"> Privacy </a>
</div>
</footer>
<script src = "~/lib/jquery/dist/jquery.min.js"> </script>
<script src = "~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"> </script>
<script src = "~/js/site.js" asp-append-version = "true"> </script>
@RenderSection( "Scripts", required: false )
</body>
</html>
在「MyRazorWeb」Razor Page網站專案加入「Create」Razor Page。在「Solution Explorer」視窗,「MyRazorPage」專案 - 「Pages \ Books」資料夾項目上方,按滑鼠右鍵,從快捷選單選擇「Add」- 「Razor Page」項目,請參考下圖所示:

圖 1:加入「Razor Page」項目。
在「Add New Scaffolded Item」對話盒左方選取「Razor Pages」分類,然後選取「Razor Page」項目,按下「Add 」按鈕,請參考下圖所示:

圖 2:選取「Razor Page」項目。
在「Add Razor Page」對話盒中,設定Razor Page名稱為「Create」,勾選下方「Generate PageModel class」以及「Use a layout page」項目;然後按下「Add 」按鈕,請參考下圖所示:

圖 3:「Add Razor Page」對話盒。
在Visual Studio工具產生的「Create」Razor Page類別程式檔案加入以下程式碼,在「OnPost」方法中新增資料到集合中:
MyRazorWeb\Pages\Books\Create.cshtml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using MyModels;
using MyServices;
namespace MyRazorWeb.Pages.Books {
public class CreateModel : PageModel {
private readonly IBookRepository bookRepository;
public CreateModel( IBookRepository bookRepository ) {
this.bookRepository = bookRepository;
}
[BindProperty]
public Book Book { get; set; }
public IActionResult OnGet() {
return Page();
}
public IActionResult OnPost() {
if ( !ModelState.IsValid ) {
return Page();
}
Book = bookRepository.Create( Book );
return RedirectToPage( "List" );
}
}
}
修改「Create.cshtml」檔案中的程式碼,加入「Form」標簽,在標簽中利用標記協助程式(Tag Helper)來產生文字方塊、核取方塊與Select清單欄位:
MyRazorWeb\Pages\Books\Create.cshtml
@page
@model MyRazorWeb.Pages.Books.CreateModel
@{
ViewData["Title"] = "Create";
}
<h1> Book Create </h1>
<hr />
<div class = "row">
<div class = "col-md-8">
<form method = "post">
<div asp-validation-summary = "All" class = "text-danger"> </div>
<div class = "form-group">
<label asp-for = "Book.Title" class = "control-label"> </label>
<input asp-for = "Book.Title" class = "form-control" />
<span asp-validation-for = "Book.Title" class = "text-danger"> </span>
</div>
<div class = "form-group">
<label asp-for = "Book.Price" class = "control-label"> </label>
<input asp-for = "Book.Price" class = "form-control" />
<span asp-validation-for = "Book.Price" class = "text-danger"> </span>
</div>
<div class = "form-group">
<label asp-for = "Book.PublishDate" class = "control-label"> </label>
<input asp-for = "Book.PublishDate" type = "date" class = "form-control" />
<span asp-validation-for = "Book.PublishDate" class = "text-danger"> </span>
</div>
<div class = "form-group form-check">
<label class = "form-check-label">
<input class = "form-check-input" asp-for = "Book.InStock" /> @Html.DisplayNameFor( model => model.Book.InStock )
</label>
</div>
<div class = "form-group">
<label asp-for = "Book.Description" class = "control-label"> </label>
<input asp-for = "Book.Description" class = "form-control" />
<span asp-validation-for = "Book.Description" class = "text-danger"> </span>
</div>
<div class = "form-group">
<label asp-for = "Book.Category" class = "control-label"> </label>
<select asp-for = "Book.Category" class = "form-control"
asp-items = "Html.GetEnumSelectList<MyModels.Category>()">
<option value = ""> Please Select </option>
</select>
<span asp-validation-for = "Book.Category" class = "text-danger"> </span>
</div>
<div class = "form-group">
<input type = "submit" value = "Create" class = "btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-page = "List"> Back to List </a>
</div>
@section Scripts {
@{ await Html.RenderPartialAsync( "_ValidationScriptsPartial" ); }
}
在新增「Create」Razor Page中,「PublishDate」Input欄位有稍做了一些修改,我們只需要填寫年、月、日,不需要時、分、秒,我們在標記協助程式中,設定「type="data"」來解決這個問題,參考以下程式碼:
<input asp-for = "Book.PublishDate" type = "date" class = "form-control" />
選取Visual Studio開發工具「Build」-「Build Solution」項目編譯目前的專案,確認程式碼能正確編譯。在Visual Studio開發工具,按CTRL+F5執行網站「Books/Create」Razor Page,執行結果參考如下,在表單欄位中輸入要新增的圖書資訊:

圖 4:新增資料。
若輸入的資料不符所需,以會透過驗證機制來顯示錯誤訊息,請參考下圖所示:

圖 5:資料驗證。
資料新增完成後將回到「List」頁面,顯示最新圖書清單資料,從中可以看到圖書的最新流水號碼,請參考下圖所示:

圖 6:顯示新增後的資料。
遠端驗證
現在新增資料的功能有一個新的需求,若圖書的名稱重複了,將視為錯誤,我們希望使用者輸入圖書名稱後就馬上進行檢查,不用等到圖書名稱、價格、出版日期、庫存、說明與圖書分類資料全都輸完提交之後,再進行檢查。
ASP.NET Core 3.x版新增一個「PageRemote」Attribute可以啟用遠端驗證(Remote Validation)的功能,所謂遠端驗證是指利用JavaScript程式碼將使用者輸入的資料,提交到伺服端程式做檢查,我們可以利用遠端驗證來達到新增資料的新需求。
Razor Pages遠端驗證功能要能生效,需要滿足以下需求:
- 要透過jQuery Unobtrusive 驗證程式庫。Razor Pages範本專案中,已經安裝好這個套件了。
- 在模型屬性上方套用「PageRemote」Attribute。
- 在伺服端要撰寫Page Handler來處理驗證的動作。
- 必需直接套用到PageModel類別的屬性;不可以是屬性值的子屬性(巢狀屬性)。
最後一個需求有一點麻煩,檢視「CreateModel」程式碼,我們想要驗證的恰巧是「CreateModel」類別「Book」屬性的「Title」屬性,也就是巢狀屬性。為此,我們需要變動模型的程式碼,修改Book」類別程式如下,將「Book」類別「Title」屬性的驗證Attribute程式碼註解,搬到「CreateModel」類別的「Title」屬性:
MyModel\Book.cs
using Microsoft.AspNetCore.Mvc.ModelBinding;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
namespace MyModels {
public class Book {
[Display( Name = "圖書編號" )]
public int Id { get; set; }
[Display( Name = "圖書名稱" )]
//[Required( ErrorMessage = "圖書名稱不可為空白" )]
//[MaxLength( 50 , ErrorMessage = "長度不可超過 {0}" )]
public string Title { get; set; }
[Display( Name = "價格" )]
[Range( 1 , int.MaxValue , ErrorMessage = "{0} 有效範圍在 {1} 與 {2} 之間" )]
public int Price { get; set; }
[Display( Name = "出版日期" )]
public DateTime PublishDate { get; set; }
[Display( Name = "庫存" )]
public bool InStock { get; set; }
[Display( Name = "說明" )]
[MaxLength( 50 , ErrorMessage = "長度不可超過 {0}" )]
public string Description { get; set; }
[Display( Name = "圖書分類" )]
public Category? Category { get; set; }
}
}
在「CreateModel」類別中新增一個「Title」屬性,然後加上原來放在「Book」類別「Title」屬性上的套用「Required」、「MaxLength」驗證Attribute,同時套用「PageRemote」Attribute:
[Required( ErrorMessage = "圖書名稱不可為空白" )]
[MaxLength( 50 , ErrorMessage = "長度不可超過 {0}" )]
[BindProperty, PageRemote (
ErrorMessage = "已存在重複的圖書名稱" ,
AdditionalFields = "__RequestVerificationToken" ,
HttpMethod = "post" ,
PageHandler = "CheckTitle"
)]
public string Title { get; set; }
「PageRemote」Attribute包含以下常用屬性:
- ErrorMessage:自訂錯誤訊息。
- AdditionalFields:以逗號區隔的額外欄位名稱。以HTTP Post送出驗證請求時,必需包含當前age的Request Verification Token,否則會得到400號錯誤。
- HttpMethod:要使用來送出請求的方式,預設是HTTP Get,可以設Get或Post。
- PageHandler:用來進行驗證的Page Handler名稱。
- PageName: Page名稱,若省略為目前使用的Page。
在「CreateModel」類別中新增一個「OnPostCheckTitle」方法,撰寫驗證邏輯,判斷圖書名稱是否已存在於集合中:
public JsonResult OnPostCheckTitle() {
var valid = !( this.bookRepository.GetAllBooks().Count( b => b.Title == Title ) > 0 );
return new JsonResult( valid );
}
這個方法必需回傳一個JSON格式的資料,來指明驗證的結果是成功(true)還是失敗(false)。還有一點要注意的是資料提交之後,在「OnPost」方法,需要將「Title」屬性的值更新到「Book.Title」屬性:
public IActionResult OnPost() {
if ( !ModelState.IsValid ) {
return Page();
}
Book.Title = Title;
Book = bookRepository.Create( Book );
return RedirectToPage( "List" );
}
Create.cshtml.cs完整的程式碼如下:
MyRazorWeb\Pages\Books\Create.cshtml.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using MyModels;
using MyServices;
namespace MyRazorWeb.Pages.Books {
public class CreateModel : PageModel {
private readonly IBookRepository bookRepository;
public CreateModel( IBookRepository bookRepository ) {
this.bookRepository = bookRepository;
}
[BindProperty]
public Book Book { get; set; }
[Required( ErrorMessage = "圖書名稱不可為空白" )]
[MaxLength( 50 , ErrorMessage = "長度不可超過 {0}" )]
[BindProperty, PageRemote(
ErrorMessage = "已存在重複的圖書名稱" ,
AdditionalFields = "__RequestVerificationToken" ,
HttpMethod = "post" ,
PageHandler = "CheckTitle"
)]
public string Title { get; set; }
public JsonResult OnPostCheckTitle() {
var valid = !( this.bookRepository.GetAllBooks().Count( b => b.Title == Title ) > 0 );
return new JsonResult( valid );
}
public IActionResult OnGet() {
return Page();
}
public IActionResult OnPost() {
if ( !ModelState.IsValid ) {
return Page();
}
Book.Title = Title;
Book = bookRepository.Create( Book );
return RedirectToPage( "List" );
}
}
}
接著改寫「Create.cshtml」Razor Page顯示圖書名稱的 Input欄位之程式碼如下:
<label asp-for = "Book.Title" class = "control-label"> </label>
<input asp-for = "Title" class = "form-control" />
<span asp-validation-for = "Title" class = "text-danger"> </span>
Create.cshtml.cs完整的程式碼如下:
MyRazorWeb\Pages\Books\Create.cshtml
@page
@model MyRazorWeb.Pages.Books.CreateModel
@{
ViewData["Title"] = "Create";
}
<h1> Book Create </h1>
<hr />
<div class = "row">
<div class = "col-md-8">
<form method = "post">
<div asp-validation-summary = "All" class = "text-danger"> </div>
<div class = "form-group">
<label asp-for = "Book.Title" class = "control-label"> </label>
<input asp-for = "Title" class = "form-control" />
<span asp-validation-for = "Title" class = "text-danger"> </span>
@*
<label asp-for = "Book.Title" class = "control-label"> </label>
<input asp-for = "Book.Title" class = "form-control" />
<span asp-validation-for = "Book.Title" class = "text-danger"> </span>
*@
</div>
<div class = "form-group">
<label asp-for = "Book.Price" class = "control-label"> </label>
<input asp-for = "Book.Price" class = "form-control" />
<span asp-validation-for = "Book.Price" class = "text-danger"> </span>
</div>
<div class = "form-group">
<label asp-for = "Book.PublishDate" class = "control-label"> </label>
<input asp-for = "Book.PublishDate" type = "date" class = "form-control" />
<span asp-validation-for = "Book.PublishDate" class = "text-danger"> </span>
</div>
<div class = "form-group form-check">
<label class = "form-check-label">
<input class = "form-check-input" asp-for = "Book.InStock" /> @Html.DisplayNameFor( model => model.Book.InStock )
</label>
</div>
<div class = "form-group">
<label asp-for = "Book.Description" class = "control-label"> </label>
<input asp-for = "Book.Description" class = "form-control" />
<span asp-validation-for = "Book.Description" class = "text-danger"> </span>
</div>
<div class = "form-group">
<label asp-for = "Book.Category" class = "control-label"> </label>
<select asp-for = "Book.Category" class = "form-control"
asp-items = "Html.GetEnumSelectList<MyModels.Category>()">
<option value = ""> Please Select </option>
</select>
<span asp-validation-for = "Book.Category" class = "text-danger"> </span>
</div>
<div class = "form-group">
<input type = "submit" value = "Create" class = "btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-page = "List"> Back to List </a>
</div>
@section Scripts {
@{ await Html.RenderPartialAsync( "_ValidationScriptsPartial" ) ; }
}
選取Visual Studio開發工具「Build」-「Build Solution」項目編譯目前的專案,確認程式碼能正確編譯。在Visual Studio開發工具,按CTRL+F5執行網站「Books/Create」Razor Page,執行結果參考如下,在表單欄位中輸入要新增的圖書資訊,若圖書的名稱重複了,只要按Tab鍵企圖讓游標移到下一個欄位時,就會進行檢查:

圖 7:遠端驗證。
除了「Title」欄位之外,「Book」類別的其它屬性也能正常的進行驗證,請參考下圖所示:

圖 8:資料驗證。
編輯資料
修改了MyModel\Book.cs類別的程式之後,我們圖書資料編輯的程式碼便無法驗證「Title」屬性的值是否正確,讓我們來修訂這個問題,首先修改「EditModel」程式碼,加入一個「Title」屬性,並套用「Required」、「MaxLength」驗證Attribute,同時套用「PageRemote」Attribute:
[Required( ErrorMessage = "圖書名稱不可為空白" )]
[MaxLength( 50 , ErrorMessage = "長度不可超過 {0}" )]
[BindProperty, PageRemote(
ErrorMessage = "已存在重複的圖書名稱" ,
AdditionalFields = "__RequestVerificationToken" ,
HttpMethod = "post" ,
PageHandler = "CheckTitle"
)]
public string Title { get; set; }
和資料新增不太一樣,資料編輯時,圖書名稱可以不修改,因此遠端驗證的Page Handler程式碼要修改如下:
public JsonResult OnPostCheckTitle() {
var id = RouteData.Values["id"].ToString();
var valid = !( this.bookRepository.GetAllBooks().Count( b => b.Title == Title && b.Id.ToString() != id ) > 0 );
return new JsonResult( valid );
}
Edit.cshtml.cs完整的程式碼如下:
MyRazorWeb\Pages\Books\Edit.cshtml.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using MyModels;
using MyServices;
namespace MyRazorWeb.Pages.Books {
public class EditModel : PageModel {
private readonly IBookRepository bookRepository;
public EditModel( IBookRepository bookRepository ) {
this.bookRepository = bookRepository;
}
[BindProperty]
public Book Book { get; set; }
[Required( ErrorMessage = "圖書名稱不可為空白" )]
[MaxLength( 50 , ErrorMessage = "長度不可超過 {0}" )]
[BindProperty, PageRemote(
ErrorMessage = "已存在重複的圖書名稱" ,
AdditionalFields = "__RequestVerificationToken" ,
HttpMethod = "post" ,
PageHandler = "CheckTitle"
)]
public string Title { get; set; }
public JsonResult OnPostCheckTitle() {
var id = RouteData.Values["id"].ToString();
var valid = !( this.bookRepository.GetAllBooks().Count( b => b.Title == Title && b.Id.ToString() != id ) > 0 );
return new JsonResult( valid );
}
public IActionResult OnGet( int id ) {
Book = bookRepository.GetBook( id );
Title = Book.Title;
if ( Book == null ) {
return RedirectToPage( "/NotFound" );
}
return Page();
}
public IActionResult OnPost() {
if ( !ModelState.IsValid ) {
return Page();
}
Book.Title = Title;
Book = bookRepository.Update( Book );
return RedirectToPage( "List" );
}
}
}
接著改寫「Create.cshtml」Razor Page顯示圖書名稱的 Input欄位之程式碼如下:
<label asp-for = "Book.Title" class = "control-label"> </label>
<input asp-for = "Title" class = "form-control" />
<span asp-validation-for = "Title" class = "text-danger"> </span>
Edit.cshtml.cs完整的程式碼如下:
MyRazorWeb\Pages\Books\Edit.cshtml
@page "{id}"
@model MyRazorWeb.Pages.Books.EditModel
@{
ViewData["Title"] = "Edit";
}
<h1> Book Edit</h1>
<hr />
<div class = "row">
<div class = "col-md-8">
<form method = "post">
<div asp-validation-summary = "All" class = "text-danger"> </div>
<input type = "hidden" asp-for = "Book.Id" />
<div class = "form-group">
<label asp-for = "Book.Title" class = "control-label"> </label>
<input asp-for = "Title" class = "form-control" />
<span asp-validation-for = "Title" class = "text-danger"> </span>
@*
<label asp-for = "Book.Title" class = "control-label"> </label>
<input asp-for = "Book.Title" class = "form-control" />
<span asp-validation-for = "Book.Title" class = "text-danger"> </span>
*@
</div>
<div class = "form-group">
<label asp-for = "Book.Price" class = "control-label"> </label>
<input asp-for = "Book.Price" class = "form-control" />
<span asp-validation-for = "Book.Price" class = "text-danger"> </span>
</div>
<div class = "form-group">
<label asp-for = "Book.PublishDate" class = "control-label"> </label>
<input asp-for = "Book.PublishDate" class = "form-control" />
<span asp-validation-for = "Book.PublishDate" class = "text-danger"> </span>
</div>
<div class = "form-group form-check">
<label class = "form-check-label">
<input class = "form-check-input" asp-for = "Book.InStock" /> @Html.DisplayNameFor( model => model.Book.InStock )
</label>
</div>
<div class = "form-group">
<label asp-for = "Book.Description" class = "control-label"> </label>
<input asp-for = "Book.Description" class = "form-control" />
<span asp-validation-for = "Book.Description" class = "text-danger"> </span>
</div>
<div class = "form-group">
<label asp-for = "Book.Category" class = "control-label"> </label>
<select asp-for = "Book.Category" class = "form-control"
asp-items = "Html.GetEnumSelectList<MyModels.Category>()">
<option value = ""> Please Select </option>
</select>
<span asp-validation-for = "Book.Category" class = "text-danger"> </span>
</div>
<div class = "form-group">
<input type = "submit" value = "Save" class = "btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-page = "./List"> Back to List </a>
</div>
@section Scripts {
<partial name = "_ValidationScriptsPartial" />
}
現在選取Visual Studio開發工具「Build」-「Build Solution」項目編譯目前的專案,確認程式碼能正確編譯。在Visual Studio開發工具,按CTRL+F5執行網站「Books/List」Razor Page,執行結果參考如下,點選某筆圖書後方的「Edit」超連結時,「Book」物件的「id」屬性值就以查詢字串的型式,傳遞到「Edit」Razor Page,若試圖將2號資料的圖書名稱改為和3號資料一樣的「Marvel」便自動顯示遠端驗證錯誤訊息:

圖 9:資料編輯遠端驗證功能。
然後確認其它欄位資料是否可以被驗證,或正確地儲存,請參考下圖所示:

圖 10:驗證資料。