介绍 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 方法。 (有关 和 POST 操作之间的差异GET的详细信息,请参阅上一教程中的GET、POST 和 HTTP 谓词安全边栏。)

请注意,每个文本框都有一个 name 元素 (titlegenreyear) 。 如上一教程所示,这些名称非常重要,因为必须具有这些名称,以便稍后可以获取用户的输入。 可以使用任何名称。 使用有意义的名称有助于记住正在使用的数据。

value每个<input>元素的 属性都包含一些 Razor 代码 (例如 Request.Form["title"] ,) 。 在上一教程中,你了解了此技巧的一个版本,以保留输入到文本框中的值, (提交表单后是否有任何) 。

获取表单值

接下来,添加用于处理窗体的代码。 在大纲中,你将执行以下操作:

  1. 检查是否在提交) 提交 (发布页面。 你希望代码仅在用户单击按钮时运行,而不是在页面首次运行时运行。
  2. 获取用户在文本框中输入的值。 在这种情况下,由于窗体使用 POST 谓词,因此从集合中 Request.Form 获取窗体值。
  3. 将值作为新记录插入到 Movies 数据库表中。

在该文件的顶部,添加以下代码:

@{
    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> 元素的名称一样,可以按你喜欢的任何方式调用它们。 (变量的名称不必与 form.) 上元素的名称属性 <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”。 (或任意内容。) 然后单击“ 添加影片”。

如果一切顺利,你会重定向到“ 电影 ”页面。 确保新电影已列出。

显示新添加的影片的“电影”页面

验证用户输入

返回 AddMovie 页,或再次运行它。 输入另一部电影,但这次只输入标题,例如,输入“雨中的辛金”。 然后单击“ 添加影片”。

你将再次重定向到“ 电影 ”页面。 你可以找到新电影,但它不完整。

显示缺少某些值的新电影的“电影”页

创建 Movies 表时,明确指出任何字段都不能为 null。 此处有一个新电影的输入表单,并且将字段留空。 这是一个错误。

在这种情况下,数据库实际上不会引发 (或 引发 错误) 。 你没有提供流派或年份,因此 AddMovie 页中的代码将这些值视为所谓的 空字符串。 当 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,何时仅为空字符串。 在 AddMovie 页的代码中,通过使用 Request.Form["title"] 等获取文本框的值。 在单击) 按钮之前第一次 (运行页面时,的 Request.Form["title"] 值为 null。 但在提交表单时, Request.Form["title"] 获取文本框的值 title 。 这并不明显,但空文本框不为 null;它只包含一个空字符串。 因此,当代码在响应按钮单击时, Request.Form["title"] 其中包含一个空字符串。

为什么这种区别很重要? 创建 Movies 表时,明确指出任何字段都不能为 null。 但在这里,你有一个新电影的输入表单,并且你将字段留空。 当你尝试保存没有流派或年份值的新电影时,你会合理地期望数据库抱怨。 但这就是重点 - 即使将这些文本框留空,值也不会为 null;它们是空字符串。 因此,可以将新电影保存到数据库中,这些列为空,但不能为 null! — 值。 因此,必须确保用户不会提交空字符串,可以通过验证用户的输入来执行此操作。

验证帮助程序

ASP.NET 网页包括一个帮助程序(Validation即帮助程序),可用于确保用户输入符合要求的数据。 帮助Validation程序是内置于 ASP.NET 网页的帮助程序之一,因此你不必使用 NuGet 将其安装为包,就像在前面的教程中安装 Gravatar 帮助程序一样。

若要验证用户的输入,需要执行以下操作:

  • 使用代码指定需要页面上文本框中的值。
  • 将测试放入代码中,以便仅当一切正确验证时,电影信息才会添加到数据库中。
  • 将代码添加到标记中以显示错误消息。

AddMovie 页的代码块中,在变量声明前的顶部添加以下代码:

Validation.RequireField("title", "You must enter a title");
Validation.RequireField("genre", "Genre is required");
Validation.RequireField("year", "You haven't entered a year");

Validation.RequireField对于需要输入的每个字段 (<input>元素) 调用一次。 还可以为每个调用添加自定义错误消息,如下所示。 (我们更改了消息,只是为了表明你可以把任何喜欢的东西放在那里。)

如果出现问题,需要阻止将新的电影信息插入数据库。 在 块中 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 方法显示列表中 (元素) <ul> 元素中的所有 <div> 验证消息。 与 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-errorCSS 样式类。 若要定义查找验证摘要,请创建名为 的 validation-summary-errorsCSS 样式类。

若要查看此方法的工作原理,请在页面的 部分内添加 元素<style><head>。 然后定义包含以下规则的名为 field-validation-errorvalidation-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 file.)

如果存在验证错误, Html.ValidationMessage 方法将呈现包含 <span>class="field-validation-error"的元素。 通过为该类添加样式定义,可以配置消息的外观。 如果存在错误,该方法 ValidationSummary 同样会动态呈现属性 class="validation-summary-errors"

再次运行页面,并故意省略几个字段。 错误现在更加明显。 (事实上,它们被过度使用,但这只是为了说明你可以做什么。)

显示已设置样式的验证错误的“添加影片”页

最后一步是方便从原始电影列表访问 AddMovie 页面。

再次打开 “电影 ”页面。 在帮助程序后面的结束 </div> 标记之后 WebGrid ,添加以下标记:

<p>
  <a href="~/AddMovie">Add a movie</a>
</p>

如前所述,ASP.NET 将 ~ 运算符解释为网站的根。 不必使用 ~ 运算符;可以使用 标记 <a href="./AddMovie">Add a movie</a> 或其他某种方法来定义 HTML 理解的路径。 但是, ~ 当您为 Razor 页面创建链接时,运算符是一种很好的常规方法,因为它使网站更加灵活 - 如果将当前页移动到子文件夹,该链接仍将转到 AddMovie 页面。 (请记住, ~ 运算符仅适用于 .cshtml 页。ASP.NET 理解它,但它不是标准 HTML.)

完成后,运行“ 电影 ”页。 它如下所示:

包含指向“添加电影”页面链接的“电影”页面

单击 “添加影片” 链接,确保转到 “添加影片 ”页面。

即将上一步

在下一教程中,你将了解如何让用户编辑数据库中已有的数据。

AddMovie 页面的完整列表

@{

    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>

其他资源