共用方式為


ASP.NET 網頁簡介 - 使用表單輸入資料庫資料

Tom FitzMacken

本教學課程說明如何建立項目表單,然後在您使用 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 方法。 (如需了解 GETPOST 作業之間的差異,請參閱上一個教學課程中的 GET、POST 和 HTTP 動詞切換側邊列。)

請注意,每個文字方塊都有一個 name 元素 (titlegenreyear)。 如您在上一個教學課程中所見,這些名稱很重要,因為您必須擁有這些名稱,以便稍後取得使用者的輸入。 您可以使用任何名稱。 使用有意義的名稱有助於記住您正在處理的資料。

每個 <input> 元素的 value 屬性都包含一點 Razor 程式碼 (例如:Request.Form["title"])。 您已在上一個教學課程中瞭解此訣竅的版本,以在提交表單之後保留文字方塊中輸入的值 (若有)。

取得表單值

接下來,您會新增處理表單的程式碼。 在大綱中,您將執行下列動作:

  1. 檢查頁面是否已張貼 (已提交)。 只有當使用者按下按鈕,而不是第一次執行頁面時,您才要執行程式碼。
  2. 取得使用者輸入文字方塊的值。 在此案例下,因為表單正在使用 POST 動詞,因此您會從 Request.Form 集合中取得表單值。
  3. 將值插入電影資料庫資料表中做為新記錄。

在檔案的頂端,新增下列程式碼:

@{
    var title = "";
    var genre = "";
    var year = "";

    if(IsPost){
        title = Request.Form["title"];
        genre = Request.Form["genre"];
        year = Request.Form["year"];
    }
}

前幾行會建立變數 (titlegenreyear) 以保留文字方塊中的值。 這行 if(IsPost) 可確保只有在使用者按一下 [新增電影] 按鈕,也就是張貼表單後,才設定變數。

如先前教學課程中所見,您會使用類似 Request.Form["name"] 的運算式來取得文字方塊的值,其中 name<input> 元素的名稱。

變數的名稱 (titlegenreyear) 是任意的。 就像您指派給 <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>

其他資源