ASP.NET Web ページの概要 - フォームを使用したデータベース データの入力

作成者: Tom FitzMacken

このチュートリアルでは、入力フォームを作成し、そのフォームから取得したデータをデータベース テーブルに入力する方法を、ASP.NET Web ページ (Razor) を使用する場合について説明します。 ここでは、シリーズを「ASP.NET Web ページ での HTML フォームの基礎」まで完了していることを前提としています。

ここでは、次の内容について学習します。

  • 入力フォームの処理方法についての詳細。
  • データベースにデータを追加 (挿入) する方法。
  • ユーザーがフォームに必要な値を入力したことを確認する方法 (ユーザー入力を検証する方法)。
  • 検証エラーを表示する方法。
  • 現在のページから別のページにジャンプする方法。

対象機能またはテクノロジ:

  • Database.Execute メソッド。
  • SQL Insert Into ステートメント
  • Validation ヘルパー。
  • Response.Redirect メソッド。

作成するアプリケーション:

前のチュートリアルでは、データベースを作成する方法を示しました。データベース ワークスペースで作業して、WebMatrix でデータベースを直接編集してデータベース データを入力しました。 ただし、ほとんどのアプリでは、これはデータをデータベースに格納する実用的な方法ではありません。 そのため、このチュートリアルでは、データを入力してデータベースに保存できる Web ベースのインターフェイスを作成します。

新しい映画を入力できるページを作成します。 このページには、映画のタイトル、ジャンル、年を入力できるフィールド (テキスト ボックス) がある入力フォームが含まれます。 ページは次のページのようになります。

'Add Movie' page in browser

テキスト ボックスは、次のマークアップのような 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> 要素で method 属性の値として post が使用されていることに注目してください。 前のチュートリアルでは、get メソッドを使用するフォームを作成しました。 フォームからサーバーへの値の送信は行われたものの、要求によって変更が行われることはなかったため、これは正しい処理でした。 そこで行ったことは、さまざまな方法でデータをフェッチすることでした。 しかしながら、このページでは、では変更を "行います"。新しいデータベース レコードの追加です。 そのため、このフォームでは post メソッドを使用することになります。 (GET 操作と POST 操作の違いの詳細については、以前のチュートリアルの GET、POST、HTTP 動詞の安全性サイドバーを参照してください)

各テキスト ボックスには name 要素 (titlegenreyear) があることに注意してください。 前のチュートリアルで説明したように、これらの名前は重要です。後でユーザーの入力を取得できるようにするためには、これらの名前が付いている必要があります。 任意の名前を使用できます。 使用しているデータを覚えておくのに役立つわかりやすい名前を使用すると便利です。

<input> 要素の value 属性には、少量の 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> 要素に割り当てる名前と同様に 任意の名前を付けることができます。 (変数の名前は、フォーム上の <input> 要素の name 属性と一致している必要はありません)ただし、<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 ステートメントに連結することは、「ASP.NET Web ページのフォームの基礎」 (前のチュートリアル) で説明しているように、SQL インジェクション攻撃に自らをさらすことになります。

引き続き if ブロック内で、db.Execute の行の後に次の行を追加します。

Response.Redirect("~/Movies");

新しい映画がデータベースに挿入されると、この行によって Movies ページにジャンプ (リダイレクト) され、入力した映画が表示されます。 ~ 演算子は "Web サイトのルート" を意味します (~ 演算子は 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");
    }
}

Insert コマンドのテスト (ここまでの内容)

まだ完了していませんが、ここでテストしておきましょう。

WebMatrix のファイルのツリー ビューで AddMovie.cshtml ページを右クリックし、[ブラウザーで起動] をクリックします。

Screenshot shows the 'Add Movie' page in browser.

(ブラウザーで別のページが表示される場合は、URL が http://localhost:nnnnn/AddMovie であることを確認してください)。ここで nnnnn は使用しているポート番号です)

エラー ページが表示されましたか? その場合は、コードが前掲のとおりであることを、注意深く読んで確認してください。

"Citizen Kane"、"Drama"、"1941" などを使用して、フォームで映画を入力します。 (何でも結構です)続いて [映画の追加] をクリックします。

問題がなければ、Movies ページにリダイレクトされます。 入力した新しい映画が一覧に表示されることを確認します。

Movies page showing newly added movie

ユーザー入力の検証

AddMovie ページに戻るか、もう一度実行します。 別の映画を入力しますが、今回はタイトルのみを入力します。たとえば、「Singin' in the Rain」と入力します。 続いて [映画の追加] をクリックします。

Movies ページに再びリダイレクトされます。 新しい映画が表示されますが、不完全です。

Movies page showing new movie that is missing some values

Movies テーブルを作成したときに、どのフィールドも null にできないと明示的に指定しました。 ここでは、新しい映画の入力フォームがあり、フィールドを空白のままにしています。 これはエラーです。

この場合、データベースは実際にはエラーを発生 (または "スロー") させませんでした。 ジャンルや年を指定していないので、AddMovie ページのコードでは、これらの値をいわゆる "空の文字列" として扱いました。 SQL Insert Into コマンドが実行されたとき、genre フィールドと year フィールドには有用なデータはありませんでしたが、null ではありませんでした。

ユーザーが半分空の映画情報をデータベースに入力することが望ましくないのは明らかです。 解決策は、ユーザーの入力を検証することです。 検証では最初に、ユーザーがすべてのフィールドの値を入力したことを確認します (つまり、空の文字列が含まれていないことを確認します)。

ヒント

Null 文字列と空の文字列

プログラミングでは、"値なし" という異なる概念に区別があります。一般に、何らかの方法による設定も初期化もされていない場合、値は null です。 これに対し、文字データ (文字列) を受け取る変数は "空の文字列" に設定されることがあります。 その場合、値は null ではありません。長さが 0 の文字の文字列に明示的に設定されているだけです。 次の 2 つのステートメントでその違いを示しています。

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 です。 しかし、フォームを送信する時点で、title テキスト ボックスの値が Request.Form["title"] によって取得されます。 わかりにくいかもしれませんが、空のテキスト ボックスは null ではありません。ただ空の文字列が含まれています。 そのため、ボタンのクリックに応答してコードが実行されると、Request.Form["title"] には空の文字列が含まれています。

この区別が重要なのはなぜでしょうか。 Movies テーブルを作成したときに、どのフィールドも null にできないと明示的に指定しました。 けれども、ここでは、新しい映画の入力フォームがあり、フィールドを空白のままにしています。 合理的な想定として、ジャンルや年に値が入っていない新しい映画を保存しようとしたときに、データベースが異議を唱えることが考えられます。 しかし、それが重要な点です。これらのテキスト ボックスを空白のままにしても、値は null ではありません。空の文字列です。 その結果、これらの列が空の値 — ただし決して null ではない — として、新しいムービーをデータベースに保存できます。 — 値。 そのため、ユーザーが空の文字列を送信しないようにする必要があります。ユーザーの入力を検証することで、これを実行できます。

検証ヘルパー

ASP.NET Web ページにはヘルパー (Validation ヘルパー) が含まれており、ユーザーが確実に要件を満たすデータを入力するようにするために使用できます。 Validation ヘルパーは、ASP.NET Web ページに組み込まれているヘルパーの 1 つであるため、(前のチュートリアルで Gravatar ヘルパーをインストールした方法である) NuGet を使用してパッケージとしてインストールする必要はありません。

ユーザーの入力を検証するには、次のようにします。

  • コードを使用して、ページのテキスト ボックスには値の入力を必須とするよう指定します。
  • コードにテストを配置して、すべてが正しく検証された場合にのみ映画情報がデータベースに追加されるようにします。
  • エラー メッセージを表示するコードをマークアップに追加します。

AddMovie ページのコード ブロックで、変数宣言の直前の先頭に、次のコードを追加します。

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 を 1 回呼び出します。 また、次のように、呼び出しごとにカスタム エラー メッセージを追加することもできます。 (ここではメッセージをさまざまに変えていますが、これは、メッセージを任意のものにすることができることを示すことが目的です。)

問題がある場合には、新しい映画の情報がデータベースに挿入されないようにする必要があります。 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 を返します。 その場合、そのブロック内のコードは一切実行されないため、無効な映画のエントリがデータベースに挿入されることはありません。 もちろん、Movies ページへのリダイレクトは行われません。

検証コードを含む完成したコード ブロックは、次の例のようになります。

@{
    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> 要素はレンダリングされますが、その中にテキストはありません)。

次の例のように、ページ内のマークアップを変更して、ページ上の 3 つの <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>

これで終了です。 これで、映画を追加しながら、1 つ以上のフィールドをそのままにして、ページをテストできるようになりました。 実行すると、次のエラーが表示されます。

Add Movie page showing validation error messages

検証エラー メッセージのスタイル設定

エラー メッセージがあることはわかりますが、あまり目立っていません。 こういう場合に、エラー メッセージのスタイルを設定する簡単な方法があります。

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" を動的にレンダリングします。

ページをもう一度実行し、意図的に 2 つのフィールドをそのままにします。 エラーが目立つようになりました。 (実際にはやり過ぎです。ここでは、できることを示すことが目的です)

Add Movie page showing validation errors that have been styled

最後の手順は、元の映画の一覧から AddMovie ページにアクセスしやすくすることです。

Movies ページをもう一度開きます。 WebGrid ヘルパーの後にある終了 </div> タグの後に、次のマークアップを追加します。

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

前に説明したように、ASP.NET では ~ 演算子を Web サイトのルートとして解釈します。 ~ 演算子を使用する必要はありません。マークアップ <a href="./AddMovie">Add a movie</a> やその他の方法を使用して、HTML で認識されるパスを定義できます。 ただし、Razor ページのリンクを作成する場合、~ 演算子はサイトの柔軟性を高める一般的な方法です。現在のページをサブフォルダーに移動しても、リンクは引き続き AddMovie ページに移動します。 (~ 演算子は .cshtml ページ でのみ機能します。ASP.NET ではそのような動作になりますが、これは標準の HTML ではありません)

完了したら、Movies ページを実行します。 次のようになるはずです。

Movies page with link to 'Add Movies' page

[映画の追加] リンクをクリックして、AddMovie ページに移動することを確認します。

この後の内容

次のチュートリアルでは、データベースに既に存在するデータをユーザーが編集できるようにする方法について説明します。

完成した 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>

その他のリソース