ASP.NET 網頁簡介 - 使用表單輸入資料庫資料
本教學課程說明如何建立項目表單,然後在您使用 ASP.NET 網頁 (Razor) 時,將表單中的資料輸入資料庫資料表。 這是假設您已完成 ASP.NET 網頁中的 HTML 表單基本概念系列。
您將學到什麼:
- 深入瞭解如何處理項目表單。
- 如何在資料庫中新增 (插入) 資料。
- 如何確定使用者已在表單中輸入必要的值 (如何驗證使用者輸入)。
- 如何顯示驗證錯誤。
- 如何從目前頁面跳至另一個頁面。
討論的功能/技術:
Database.Execute
方法。- SQL
Insert Into
陳述式Validation
協助程式。Response.Redirect
方法。
建置內容
在之前示範如何建立資料庫的教學課程中,您是直接在 WebMatrix 中編輯資料庫,在 [資料庫] 工作區中輸入資料庫資料。 不過在大部分的應用程式中,這不是將資料放入資料庫的實際方法。 因此在本教學課程中,您將建立 Web 型介面,讓您或任何人輸入資料並將其儲存至資料庫。
您將建立一個頁面,您可以在其中輸入新的電影。 頁面將包含欄位 (文字方塊) 的項目表單,您可以在其中輸入電影標題、內容類型和年份。 頁面看起來會像此頁面:
文字方塊會是類似此標記的 HTML <input>
元素:
<input type="text" name="genre" value="" />
建立基本項目表單
建立名為 AddMovie.cshtml 的頁面。
以下列標記來取代檔案內容。 覆寫所有項目,您很快要在頂端新增程式碼區塊。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Add a Movie</title>
</head>
<body>
<h1>Add a Movie</h1>
<form method="post">
<fieldset>
<legend>Movie Information</legend>
<p><label for="title">Title:</label>
<input type="text" name="title" value="@Request.Form["title"]" />
</p>
<p><label for="genre">Genre:</label>
<input type="text" name="genre" value="@Request.Form["genre"]" />
</p>
<p><label for="year">Year:</label>
<input type="text" name="year" value="@Request.Form["year"]" />
</p>
<p><input type="submit" name="buttonSubmit" value="Add Movie" /></p>
</fieldset>
</form>
</body>
</html>
此範例顯示建立表單的一般 HTML。 它會使用適用於文字方塊和提交按鈕的 <input>
元素。 文字方塊的標題是使用標準 <label>
元素建立的。 <fieldset>
和 <legend>
元素會在表單周圍放美觀的方塊。
請注意在此頁面中,<form>
元素會使用 post
做為 method
屬性的值。 在上一個教學課程中,您已使用 get
方法建立表單。 那是正確的,因為雖然表單已將值提交至伺服器,但要求並未進行任何變更。 其所做的一切只是以不同方式擷取資料。 不過在此頁面中,您將進行變更,您將新增資料庫記錄。 因此,此表單應該使用 post
方法。 (如需了解 GET
和 POST
作業之間的差異,請參閱上一個教學課程中的 GET、POST 和 HTTP 動詞切換側邊列。)
請注意,每個文字方塊都有一個 name
元素 (title
、genre
、year
)。 如您在上一個教學課程中所見,這些名稱很重要,因為您必須擁有這些名稱,以便稍後取得使用者的輸入。 您可以使用任何名稱。 使用有意義的名稱有助於記住您正在處理的資料。
每個 <input>
元素的 value
屬性都包含一點 Razor 程式碼 (例如:Request.Form["title"]
)。 您已在上一個教學課程中瞭解此訣竅的版本,以在提交表單之後保留文字方塊中輸入的值 (若有)。
取得表單值
接下來,您會新增處理表單的程式碼。 在大綱中,您將執行下列動作:
- 檢查頁面是否已張貼 (已提交)。 只有當使用者按下按鈕,而不是第一次執行頁面時,您才要執行程式碼。
- 取得使用者輸入文字方塊的值。 在此案例下,因為表單正在使用
POST
動詞,因此您會從Request.Form
集合中取得表單值。 - 將值插入電影資料庫資料表中做為新記錄。
在檔案的頂端,新增下列程式碼:
@{
var title = "";
var genre = "";
var year = "";
if(IsPost){
title = Request.Form["title"];
genre = Request.Form["genre"];
year = Request.Form["year"];
}
}
前幾行會建立變數 (title
、genre
和 year
) 以保留文字方塊中的值。 這行 if(IsPost)
可確保只有在使用者按一下 [新增電影] 按鈕,也就是張貼表單後,才設定變數。
如先前教學課程中所見,您會使用類似 Request.Form["name"]
的運算式來取得文字方塊的值,其中 name 是 <input>
元素的名稱。
變數的名稱 (title
、genre
和 year
) 是任意的。 就像您指派給 <input>
元素的名稱一樣,您可以將其稱為任何您想要的名稱。 (變數的名稱不一定符合表單上 <input>
元素的名稱屬性)。但正如使用 <input>
元素一樣,最好使用可反映其包含資料的變數名稱。 當您撰寫程式碼時,一致的名稱可讓您更輕鬆地記住您正在處理的資料。
將資料新增至資料庫
在您剛才新增的程式碼區塊中,就在 if
區塊的右大括弧 (}
) 內 (不只是程式碼區塊內),新增下列程式碼:
var db = Database.Open("WebPagesMovies");
var insertCommand = "INSERT INTO Movies (Title, Genre, Year) VALUES(@0, @1, @2)";
db.Execute(insertCommand, title, genre, year);
此範例類似於您在上一個教學課程中用來擷取和顯示資料的程式碼。 開頭為 db =
的行會開啟資料庫,就如之前所示,下一行會定義 SQL 陳述式,也如同您先前所看到的一樣。 不過,這次會定義 SQL Insert Into
陳述式。 下列範例顯示 Insert Into
陳述式的一般語法:
INSERT INTO table (column1, column2, column3, ...) VALUES (value1, value2, value3, ...)
換句話說,您可以指定要插入的資料表,然後列出要插入的資料行,然後列出要插入的值。 (如先前所述,SQL 不區分大小寫,但有些人會利用大寫關鍵詞,更易於閱讀命令。)
您要插入的資料行已列於指令中:(Title, Genre, Year)
。 有趣的部分是如何從文字方塊取得值至命令的 VALUES
部分。 您看到的 @0
、@1
和 @2
,當然都是預留位置,而不是實際值。 當您執行命令時 (在 db.Execute
行),您會傳遞您從文字方塊取得的值。
重要! 請記住,您應該在 SQL 陳述式中包含使用者在線輸入資料的唯一方式,就是使用預留位置,如您在這裡所看到的 (VALUES(@0, @1, @2)
)。 如果您將使用者輸入串連至 SQL 陳述句,就表示可開放接受 SQL 插入式攻擊,如 ASP.NET 網頁中的表單基本概念中所述 (上一個教學課程)。
一樣地在 if
區塊內,在 db.Execute
行後面新增下列行:
Response.Redirect("~/Movies");
將新電影插入資料庫之後,這一行會讓您跳 (重新導向) 至 [電影] 頁面,可以看到您剛輸入的電影。 ~
運算子表示「網站的根目錄」。(一般而言,~
運算子只能在 ASP.NET 頁面中運作,而不是在 HTML 中運作。
完整的程式碼區塊會看起來像此範例:
@{
var title = "";
var genre = "";
var year = "";
if(IsPost){
title = Request.Form["title"];
genre = Request.Form["genre"];
year = Request.Form["year"];
var db = Database.Open("WebPagesMovies");
var insertCommand = "INSERT INTO Movies (Title, Genre, Year) Values(@0, @1, @2)";
db.Execute(insertCommand, title, genre, year);
Response.Redirect("~/Movies");
}
}
測試插入指令 (到目前為止)
您尚未完成,但現在是測試的好時機。
在 WebMatrix 中的檔案樹狀檢視中,以滑鼠右鍵按一下 [AddMovie.cshtml] 頁面,然後按一下 [在瀏覽器中啟動]。
(如果您最終在瀏覽器中有不同頁面,請確定 URL 是 http://localhost:nnnnn/AddMovie
,其中 nnnnn 是您使用的連接埠號碼。)
您是否收到錯誤頁面? 如果是,請仔細閱讀,並確定程式碼看起來完全符合先前所列的內容。
以表單輸入電影,例如,使用「大國民」、「劇情」和「1941」。 (或者任何內容)。然後按一下 [新增電影]。
如果一切順利,系統會將您重新導向至 [電影 頁面。 請確定已列出您的新電影。
驗證使用者輸入
返回 [新增電影] 頁面,或再次執行。 輸入另一部電影,但這次只輸入標題,例如「萬花嬉春」。 然後按一下 [新增電影]。
系統會再次重新導向至 [電影] 頁面。 您可以找到新的電影,但並不完整。
當您建立 [電影] 資料表時,您明確表示沒有任何字段可為 Null。 在這裡,您有新電影的項目表單,而且您留空欄位。 這是一個錯誤。
在此案例下,資料庫實際上不會引發 (或擲回) 錯誤。 您未提供內容類型或年份,因此 [新增電影] 頁面中的程式碼會將這些值視為所謂的空字串。 當執行 SQL Insert Into
命令時,內容類型和年份欄位中沒有可用的資料,但不是 Null。
顯然,您不想讓使用者在資料庫中輸入半空白的電影資訊。 解決方案是驗證使用者的輸入。 一開始,驗證只會確定使用者已輸入所有欄位的值 (也就是說,其中沒有任何欄位包含空字串)。
提示
Null 和空字串
在程式設計中,「沒有值」的不同概念是有區別的。一般而言,如果從未以任何方式設定或初始化值,則值為 null。 相反地,預期字元資料 (字串) 的變數可以設定為空字串。 在此案例下,值不是 Null,它只是明確設定為長度為零的字元字串。 這兩個陳述式會顯示差異:
var firstName; // Not set, so its value is null
var firstName = ""; // Explicitly set to an empty string -- not null
這要更複雜一點,但重要的是,null
代表一種不確定的狀態。
請不時地確切掌握何時值為 Null,以及何時值只是空字串。 在 [新增電影] 頁面的程式碼中,您可以使用 Request.Form["title"]
等取得文字方塊的值。 當第一次執行頁面時 (按下按鈕之前),Request.Form["title"]
的值會是 Null。 但是當您提交表單時,Request.Form["title"]
會取得 title
文字方塊的值。 這並不明顯,但空白文字方塊不是 Null,只是有空字串。 因此,當程式碼執行以回應按下按鈕時,Request.Form["title"]
中有空字串。
此差異有何重要性? 當您建立 [電影] 資料表時,您明確表示沒有任何字段可為 Null。 但在這裡,您有新電影的項目表單,而且您留空欄位。 當您嘗試儲存沒有內容類型或年份值的新電影時,您可合理地期望資料庫執行不順。 但這就是重點,即使您將這些文字方塊留空,值也不會是 Null,它們是空字串。 如此一來,您就可以將新電影儲存至資料庫並在這些欄位上留空白值,但不是 Null ! 因此,您必須確定使用者不會提交空字串,您可以藉由驗證使用者的輸入來執行此動作。
驗證協助程式
ASP.NET 網頁包含協助程式:Validation
協助程式,您可以用來確保使用者輸入符合您需求的資料。 Validation
協助程式是內建於 ASP.NET 網頁的協助程式之一,因此您不需要使用 NuGet 將它安裝為套件,這是您在先前教學課程中安裝 Gravatar 協助程式的方式。
若要驗證使用者的輸入,您將執行下列動作:
- 使用程式碼來指定您想在頁面上的文字方塊中要求值。
- 將測試放入程式碼中,這樣一來只有在一切都正確驗證時,才會將電影資訊新增至資料庫。
- 將程式碼新增至標記,以顯示錯誤訊息。
在 [新增電影] 頁面的程式碼區塊中,於變數宣告之前的頂端上,新增下列程式碼:
Validation.RequireField("title", "You must enter a title");
Validation.RequireField("genre", "Genre is required");
Validation.RequireField("year", "You haven't entered a year");
您要針對需要輸入項目的每個欄位 (<input>
元素) 呼叫 Validation.RequireField
一次。 您也可以為每個呼叫新增自訂錯誤訊息,就像這裡所示一樣。 (我們改變訊息只是為了顯示您可以放任何您喜歡的東西。)
如果發生問題,您想要避免新的電影資訊插入資料庫中。 在 if(IsPost)
區塊中,使用 &&
(邏輯 AND) 新增另一個測試 Validation.IsValid()
的條件。 完成時,整個 if(IsPost)
區塊看起來會像此程式碼:
if(IsPost && Validation.IsValid()){
title = Request.Form["title"];
genre = Request.Form["genre"];
year = Request.Form["year"];
var db = Database.Open("WebPagesMovies");
var insertCommand = "INSERT INTO Movies (Title, Genre, Year) Values(@0, @1, @2)";
db.Execute(insertCommand, title, genre, year);
Response.Redirect("~/Movies");
}
如果使用 Validation
協助程式登記的任何欄位發生驗證錯誤,此 Validation.IsValid
方法會傳回 false。 在此案例下,該區塊中的程式碼都不會執行,因此不會將無效的電影項目插入資料庫中。 當然,您不會被重新導向至 [電影] 頁面。
完整的程式碼區塊,包括驗證程式碼,現在看起來像下列範例:
@{
Validation.RequireField("title", "You must enter a title");
Validation.RequireField("genre", "Genre is required");
Validation.RequireField("year", "You haven't entered a year");
var title = "";
var genre = "";
var year = "";
if(IsPost && Validation.IsValid()){
title = Request.Form["title"];
genre = Request.Form["genre"];
year = Request.Form["year"];
var db = Database.Open("WebPagesMovies");
var insertCommand = "INSERT INTO Movies (Title, Genre, Year) Values(@0, @1, @2)";
db.Execute(insertCommand, title, genre, year);
Response.Redirect("~/Movies");
}
}
顯示驗證錯誤
最後一個步驟是顯示任何錯誤訊息。 您可以顯示每個驗證錯誤的個別訊息,也可以顯示摘要,或兩者均顯示。 在本教學課程中,您將執行這兩個動作,以便查看其運作方式。
在您驗證的每個 <input>
元素旁邊,呼叫 Html.ValidationMessage
方法,並將您正在驗證的 <input>
元素名稱傳遞給它。 您會將 Html.ValidationMessage
方法放在您想要顯示錯誤訊息的位置。 當執行頁面時,Html.ValidationMessage
方法會轉譯驗證錯誤所在的 <span>
元素。 (如果沒有錯誤,則會轉譯 <span>
元素,但其中沒有文字。)
變更頁面中的標記,使其包含頁面上三個 <input>
元素中每一個的 Html.ValidationMessage
方法,就像此範例:
<p><label for="title">Title:</label>
<input type="text" name="title" value="@Request.Form["title"]" />
@Html.ValidationMessage("title")
</p>
<p><label for="genre">Genre:</label>
<input type="text" name="genre" value="@Request.Form["genre"]" />
@Html.ValidationMessage("genre")
</p>
<p><label for="year">Year:</label>
<input type="text" name="year" value="@Request.Form["year"]" />
@Html.ValidationMessage("year")
</p>
若要查看摘要的運作方式,請接著在頁面上的 <h1>Add a Movie</h1>
元素後面新增下列標記和程式碼:
@Html.ValidationSummary()
根據預設,Html.ValidationSummary
方法會在清單中顯示所有驗證訊息 (<div>
元素內的 <ul>
元素)。 如同 Html.ValidationMessage
方法,驗證摘要的標記一律會轉譯,如果沒有錯誤,則不會轉譯任何清單項目。
摘要可以是顯示驗證訊息的替代方式,而不是使用 Html.ValidationMessage
方法來顯示每個欄位特定的錯誤。 或者,您可以共同使用摘要和詳細資料。 或者,您可以使用 Html.ValidationSummary
方法來顯示一般錯誤,然後使用個別 Html.ValidationMessage
呼叫來顯示詳細資料。
完整頁面現在看起來會像此範例:
@{
Validation.RequireField("title", "You must enter a title");
Validation.RequireField("genre", "Genre is required");
Validation.RequireField("year", "You haven't entered a year");
var title = "";
var genre = "";
var year = "";
if(IsPost && Validation.IsValid()){
title = Request.Form["title"];
genre = Request.Form["genre"];
year = Request.Form["year"];
var db = Database.Open("WebPagesMovies");
var insertCommand = "INSERT INTO Movies (Title, Genre, Year) Values(@0, @1, @2)";
db.Execute(insertCommand, title, genre, year);
Response.Redirect("~/Movies");
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Add a Movie</title>
</head>
<body>
<h1>Add a Movie</h1>
@Html.ValidationSummary()
<form method="post">
<fieldset>
<legend>Movie Information</legend>
<p><label for="title">Title:</label>
<input type="text" name="title" value="@Request.Form["title"]" />
@Html.ValidationMessage("title")
</p>
<p><label for="genre">Genre:</label>
<input type="text" name="genre" value="@Request.Form["genre"]" />
@Html.ValidationMessage("genre")
</p>
<p><label for="year">Year:</label>
<input type="text" name="year" value="@Request.Form["year"]" />
@Html.ValidationMessage("year")
</p>
<p><input type="submit" name="buttonSubmit" value="Add Movie" /></p>
</fieldset>
</form>
</body>
</html>
這樣就大功告成了。 您現在可以新增電影來測試頁面,但要留空一或多個欄位。 當您這麼做時,您會看到下列錯誤顯示:
設定驗證錯誤訊息的樣式
您可以看到有錯誤訊息,但並不特別明顯。 不過,有一個簡單的方式來設定錯誤訊息的樣式。
若要由 Html.ValidationMessage
設定顯示的個別錯誤訊息樣式,請建立名為 field-validation-error
的 CSS 樣式類別。 若要定義驗證摘要的外觀,請建立名為 validation-summary-errors
的 CSS 樣式類別。
若要查看這項技術的運作方式,請在頁面的 <head>
區段內新增 <style>
元素。 然後定義名為 field-validation-error
的樣式類別,且 validation-summary-errors
包含下列規則:
<head>
<meta charset="utf-8" />
<title>Add a Movie</title>
<style type="text/css">
.field-validation-error {
font-weight:bold;
color:red;
background-color:yellow;
}
.validation-summary-errors{
border:2px dashed red;
color:red;
background-color:yellow;
font-weight:bold;
margin:12px;
}
</style>
</head>
一般而言,您可能會將樣式資訊放入另外的 .css 檔案中,但為了簡單起見,您現在可以將它們放在頁面中。 (稍後在本教學課程集中,您會把 CSS 規則移至另外的 .css 檔案。)
如果發生驗證錯誤,Html.ValidationMessage
方法會轉譯包含 class="field-validation-error"
的 <span>
元素。 藉由新增該類別的樣式定義,您可以設定訊息的外觀。 如果發生錯誤,ValidationSummary
方法同樣會動態轉譯 class="validation-summary-errors"
屬性。
再次執行頁面,並刻意留空幾個欄位。 錯誤現在變得更顯而易見。 (事實上,樣式是誇大顯示,但這只是為了展示您可以做到什麼程度。)
將連結新增至「電影」頁面
最後一個步驟讓從原始電影清單進入 [新增電影] 頁面變得更容易。
再次開啟 [電影] 頁面。 在接著 WebGrid
協助程式的結尾 </div>
標籤後,新增下列標記:
<p>
<a href="~/AddMovie">Add a movie</a>
</p>
如您先前所見,ASP.NET 會將 ~
運算子解譯為網站的根目錄。 您不需要使用 ~
運算子,您可以使用標記 <a href="./AddMovie">Add a movie</a>
或其他方式來定義 HTML 了解的路徑。 但是,當您為 Razor 頁面建立連結時,~
運算子是很好的通用方法,因為它會讓網站更具彈性,如果您將目前的頁面移至子資料夾,連結仍會前往 [新增電影] 頁面。 (請記住,~
運算子只能在 .cshtml 頁面中運作。ASP.NET 瞭解它,但那不是標準的 HTML。)
完成時,請執行 [電影] 頁面。 看起來像下列此頁面:
按一下 [新增電影] 連結,確定該連結會前往 [新增電影] 頁面。
接續內容
在下一個教學課程中,您將瞭解如何讓使用者編輯資料庫中已有的資料。
新增電影頁面的完整清單
@{
Validation.RequireField("title", "You must enter a title");
Validation.RequireField("genre", "Genre is required");
Validation.RequireField("year", "You haven't entered a year");
var title = "";
var genre = "";
var year = "";
if(IsPost && Validation.IsValid()){
title = Request.Form["title"];
genre = Request.Form["genre"];
year = Request.Form["year"];
var db = Database.Open("WebPagesMovies");
var insertCommand = "INSERT INTO Movies (Title, Genre, Year) Values(@0, @1, @2)";
db.Execute(insertCommand, title, genre, year);
Response.Redirect("~/Movies");
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Add a Movie</title>
<style type="text/css">
.field-validation-error {
font-weight:bold;
color:red;
background-color:yellow;
}
.validation-summary-errors{
border:2px dashed red;
color:red;
background-color:yellow;
font-weight:bold;
margin:12px;
}
</style>
</head>
<body>
<h1>Add a Movie</h1>
@Html.ValidationSummary()
<form method="post">
<fieldset>
<legend>Movie Information</legend>
<p><label for="title">Title:</label>
<input type="text" name="title" value="@Request.Form["title"]" />
@Html.ValidationMessage("title")
</p>
<p><label for="genre">Genre:</label>
<input type="text" name="genre" value="@Request.Form["genre"]" />
@Html.ValidationMessage("genre")
</p>
<p><label for="year">Year:</label>
<input type="text" name="year" value="@Request.Form["year"]" />
@Html.ValidationMessage("year")
</p>
<p><input type="submit" name="buttonSubmit" value="Add Movie" /></p>
</fieldset>
</form>
</body>
</html>
其他資源
- 使用 Razor 語法進行 ASP.NET 網頁程式設計簡介
- W3Schools 網站上的 SQL INSERT INTO 陳述式
- 在 ASP.NET 網頁網站中驗證使用者輸入。 使用
Validation
協助程式的詳細資訊。