次の方法で共有


メンバーシップと管理

作成者: Erik Reitan

このチュートリアル シリーズでは、ASP.NET 4.5 と Microsoft Visual Studio Express 2013 for Web を使った ASP.NET Web Forms アプリケーション構築の基本について説明します。 Visual Studio 2013 の C# ソース コードを含むプロジェクトは、このチュートリアル シリーズと併せて使用できます。

このチュートリアルでは、カスタム ロールを追加するとともに ASP.NET ID を使うように Wingtip Toys サンプル アプリケーションを更新する方法について説明します。 また、カスタム ロールを持つユーザーが Web サイトから製品を追加および削除できる管理ページを実装する方法についても説明します。

ASP.NET Identity は、ASP.NET Web アプリケーションの構築に使われるメンバーシップ システムであり、ASP.NET 4.5 で使用できます。 ASP.NET Identity が使われているのは、Visual Studio 2013 Web Forms プロジェクト テンプレートに加え、ASP.NET MVCASP.NET Web APIASP.NET シングル ページ アプリケーション向けのテンプレートもあります。 また、空の Web アプリケーションから始める場合は、NuGet を使って、特に ASP.NET Identity システムをインストールすることもできます。 ただし、このチュートリアル シリーズでは、ASP.NET Identity システムを含む Web Forms projecttemplate を使います。 ASP.NET ID を使用すると、ユーザー固有のプロファイル データをアプリケーション データと簡単に統合できます。 また、ASP.NET Identity では、アプリケーション内のユーザー プロファイルの永続化モデルを選択することもできます。 データは、SQL Server データベースまたは別のデータ ストア (Windows Azure Storage テーブルなどの NoSQL データ ストアを含む) に格納できます。

このチュートリアルは、Wingtip Toys チュートリアル シリーズで前出の「精算と PayPal による支払い」というタイトルのチュートリアルに基づいています。

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

  • コードを使ってカスタム ロールとユーザーをアプリケーションに追加する方法。
  • 管理フォルダーとページへのアクセスを制限する方法。
  • カスタム ロールに属しているユーザーにナビゲーションを提供する方法。
  • モデル バインドを使って、DropDownList コントロールに製品カテゴリを設定する方法。
  • FileUpload コントロールを使ってファイルを Web アプリケーションにアップロードする方法。
  • 検証コントロールを使って入力検証を実装する方法。
  • アプリケーションから製品を追加および削除する方法。

次の機能が、このチュートリアルに含まれています。

  • ASP.NET Identity
  • 構成と承認
  • モデル バインド
  • 控えめな検証

ASP.NET Web Forms には、メンバーシップ機能があります。 既定のテンプレートを使うと、あらかじめ組み込まれているメンバーシップ機能をアプリケーション実行時にすぐに使うことができます。 このチュートリアルでは、ASP.NET Identity を使ってカスタム ロールを追加して、そのロールにユーザーを割り当てる方法について説明します。 管理フォルダーへのアクセスを制限する方法について説明します。 カスタム ロールを持つユーザーが製品を追加および削除できるページや、追加された後に製品をプレビューできるページを管理フォルダーに追加します。

カスタム ロールの追加

ASP.NET Identity を使うと、カスタム ロールの追加やそのロールへのユーザーの割り当てを、コードを使って実行できます。

  1. ソリューション エクスプローラーで、Logic フォルダーを右クリックして、新しいクラスを作成します。

  2. 新しいクラスに RoleActions.cs という名前を付けます。

  3. コードを次のように変更します。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    
    namespace WingtipToys.Logic
    {
        internal class RoleActions
        {
        }
    }
    
  4. ソリューション エクスプローラーで、Global.asax.cs ファイルを開きます。

  5. 黄色で強調表示されているコードを次のように追加して、Global.asax.cs ファイルを変更します。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Optimization;
    using System.Web.Routing;
    using System.Web.Security;
    using System.Web.SessionState;
    using System.Data.Entity;
    using WingtipToys.Models;
    using WingtipToys.Logic;
    
    namespace WingtipToys
    {
        public class Global : HttpApplication
        {
            void Application_Start(object sender, EventArgs e)
            {
              // Code that runs on application startup
              RouteConfig.RegisterRoutes(RouteTable.Routes);
              BundleConfig.RegisterBundles(BundleTable.Bundles);
    
              // Initialize the product database.
              Database.SetInitializer(new ProductDatabaseInitializer());
    
              // Create the custom role and user.
              RoleActions roleActions = new RoleActions();
              roleActions.AddUserAndRole();
            }
        }
    }
    
  6. AddUserAndRole は赤色の下線付きになっています。 AddUserAndRole コードをダブルクリックします。
    強調表示されているメソッドの先頭の文字 "A" に下線が付きます。

  7. 文字 "A" の上にカーソルを置き、AddUserAndRole メソッドのメソッド スタブを生成できる UI をクリックします。

    Membership and Administration - Generate Method Stub

  8. 次のタイトルのオプションをクリックします:
    Generate method stub for "AddUserAndRole" in "WingtipToys.Logic.RoleActions"

  9. Logic フォルダーから RoleActions.cs ファイルを開きます。
    AddUserAndRole メソッドがクラス ファイルに追加されました。

  10. RoleActions.cs ファイルを変更するには、NotImplementedException を削除し、黄色で強調表示されているコードを次のように追加します。

    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Linq;
    using System.Web;
    using WingtipToys.Models;
    using Microsoft.AspNet.Identity;
    using Microsoft.AspNet.Identity.EntityFramework;
    
    namespace WingtipToys.Logic
    {
      internal class RoleActions
      {
        internal void AddUserAndRole()
        {
          // Access the application context and create result variables.
          Models.ApplicationDbContext context = new ApplicationDbContext();
          IdentityResult IdRoleResult;
          IdentityResult IdUserResult;
    
          // Create a RoleStore object by using the ApplicationDbContext object. 
          // The RoleStore is only allowed to contain IdentityRole objects.
          var roleStore = new RoleStore<IdentityRole>(context);
    
          // Create a RoleManager object that is only allowed to contain IdentityRole objects.
          // When creating the RoleManager object, you pass in (as a parameter) a new RoleStore object. 
          var roleMgr = new RoleManager<IdentityRole>(roleStore);
    
          // Then, you create the "canEdit" role if it doesn't already exist.
          if (!roleMgr.RoleExists("canEdit"))
          {
            IdRoleResult = roleMgr.Create(new IdentityRole { Name = "canEdit" });
          }
    
          // Create a UserManager object based on the UserStore object and the ApplicationDbContext  
          // object. Note that you can create new objects and use them as parameters in
          // a single line of code, rather than using multiple lines of code, as you did
          // for the RoleManager object.
          var userMgr = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
          var appUser = new ApplicationUser
          {
            UserName = "canEditUser@wingtiptoys.com",
            Email = "canEditUser@wingtiptoys.com"
          };
          IdUserResult = userMgr.Create(appUser, ConfigurationManager.AppSettings["AppUserPasswordKey"]);
    
          // If the new "canEdit" user was successfully created, 
          // add the "canEdit" user to the "canEdit" role. 
          if (!userMgr.IsInRole(userMgr.FindByEmail("canEditUser@wingtiptoys.com").Id, "canEdit"))
          {
            IdUserResult = userMgr.AddToRole(userMgr.FindByEmail("canEditUser@wingtiptoys.com").Id, "canEdit");
          }
        }
      }
    }
    

上記のコードを実行すると、まずメンバーシップ データベースのデータベース コンテキストが確立されます。 また、メンバーシップ データベースは、App_Data フォルダー内に .mdf ファイルとしても格納されます。 最初のユーザーがこの Web アプリケーションにサインインすると、このデータベースを表示できるようになります。

Note

メンバーシップ データを製品データと共に格納する場合は、上記のコードに製品データを格納するために使ったものと同じ DbContext を使うことを検討するとよいでしょう。

internal キーワードは、型 (クラスなど) と型メンバー (メソッドやプロパティなど) のアクセス修飾子です。 Internal 型またはメンバーは、同じアセンブリ (.dll ファイル) に含まれているファイル内でのみアクセスできます。 アプリケーションを構築すると、アセンブリ ファイル (.dll) が作成されます。ここには、アプリケーション実行時に実行されるコードが含まれます。

ロール管理を提供する RoleStore オブジェクトが、データベース コンテキストに基づいて作成されます。

Note

RoleStore オブジェクトが作成されると、そこにジェネリック IdentityRole 型が使われることに注意してください。 つまり、RoleStore には IdentityRole オブジェクトのみを含めることができるという意味です。 また、ジェネリックを使うと、メモリをより適切に処理できます。

次に、作成したばかりの RoleStore オブジェクトに基づいて、RoleManager オブジェクトが作成されます。 RoleManager オブジェクトを使うと、RoleStore に対する変更の自動保存に使用できるロール関連の API が公開されます。 コードでは <IdentityRole> ジェネリック型を使うため、RoleManager には IdentityRole オブジェクトのみを含めることができます。

ユーザーは、RoleExists メソッドを呼び出して、メンバーシップ データベースに "canEdit" ロールが存在するかどうかを判断します。 存在しない場合は、ロールを作成します。

UserManager オブジェクトの作成は、RoleManager コントロールよりも複雑に見えますが、ほぼ同じです。 コードが複数行ではなく 1 行に作成されているという点のみです。 ここで渡すパラメータは、かっこに含まれる新しいオブジェクトとしてインスタンス化しています。

次に、新しい ApplicationUser オブジェクトを作成して、"canEditUser" ユーザーを作成します。 その後、ユーザーを正常に作成したら、そのユーザーを新しいロールに追加します。

Note

エラー処理は、このチュートリアル シリーズで後出の「ASP.NET エラー処理」チュートリアルで更新されます。

アプリケーションを次回起動したときに、"canEditUser" という名前のユーザーが、アプリケーションの "canEdit" という名前のロールとして追加されています。 このチュートリアルではこの後、"canEditUser" ユーザーとしてログインし、このチュートリアルで追加するその他の機能を表示します。 ASP.NET Identity に関する API の詳細については、「Microsoft.AspNet.Identity 名前空間」を参照してください。 ASP.NET Identity システムのその他の詳細については、AspnetIdentitySample に関するページを参照してください。

管理ページへのアクセスの制限

Wingtip Toys サンプル アプリケーションを使うと、匿名ユーザーとログイン ユーザーの両方が製品の表示と購入を行うことができます。 ただし、カスタムの "canEdit" ロールを持つログインしているユーザーは、製品の追加と削除のために制限付きページにアクセスすることができます。

管理フォルダーとページを追加する

次に、Wingtip Toys サンプル アプリケーションのカスタム ロールに属している "canEditUser" ユーザーのために "Admin" という名前のフォルダーを作成します。

  1. ソリューション エクスプローラーでプロジェクト名 (Wingtip Toys) を右クリックして、[追加] ->[新しいフォルダー] を選択します。
  2. 新しいフォルダーに "Admin" という名前を付けます。
  3. Admin フォルダーを右クリックして、[追加] ->[新しい項目] を選択します。
    [新しい項目の追加] ダイアログ ボックスが表示されます。
  4. 左側の [Visual C#] ->Web テンプレート グループを選択します。 中央のリストから、[マスター ページを含む Web フォーム] を選択して "AdminPage.aspx" という名前を付けたら、[追加] を選択します。
  5. Site.Master ファイルをマスター ページとして選択して、[OK] を選択します。

Web.config ファイルを追加する

Web.config ファイルを Admin フォルダーに追加すると、このフォルダー内に含まれているページへのアクセスを制限できます。

  1. Admin フォルダーを右クリックして、[追加] ->[新しい項目] を選択します。
    [新しい項目の追加] ダイアログ ボックスが表示されます。

  2. Visual C# Web テンプレートのリストの中央のリストから [Web 構成ファイル] を選択し、Web.config の既定の名前を受け入れて、[追加] を選択します。

  3. Web.config ファイルの XML の内容を次の内容で置き換えます。

    <?xml version="1.0"?>
    <configuration>
      <system.web>
        <authorization>
          <allow roles="canEdit"/>
          <deny users="*"/>
        </authorization>
      </system.web>
    </configuration>
    

Web.config ファイルを保存します。 Web.config ファイルを使うと、アプリケーションの "canEdit" ロールに属しているユーザーのみが Admin フォルダーに含まれているページにアクセスできるように指定できます。

カスタム ロール ナビゲーションを含める

カスタムの "canEdit" ロールのユーザーがアプリケーションの管理セクションに移動できるようにするには、Site.Master ページにリンクを追加する必要があります。 "canEdit" ロールに属しているユーザーのみが [管理者] リンクの確認と管理セクションへの移動を行うことができます。

  1. ソリューション エクスプローラーで、Site.Master ページを見つけて開きます。

  2. "canEdit" ロールのユーザーのためのリンクを作成するには、黄色で強調表示されているマークアップを次の記号付きリスト <ul> 要素に追加して、リストが次のように表示されるようにします。

    <ul class="nav navbar-nav">
        <li><a runat="server" id="adminLink" visible="false" 
          href="~/Admin/AdminPage">Admin</a></li>
        <li><a runat="server" href="~/">Home</a></li>
        <li><a runat="server" href="~/About">About</a></li>
        <li><a runat="server" href="~/Contact">Contact</a></li>
        <li><a runat="server" href="~/ProductList">Products</a></li>
        <li><a runat="server" href="~/ShoppingCart" 
          ID="cartCount">&nbsp;</a></li>
    </ul>
    
  3. Site.Master.cs ファイルを開きます。 黄色で強調表示されているコードを Page_Load ハンドラーに追加して、[管理者] リンクが "canEditUser" にのみ表示されるようにします。 Page_Load ハンドラーは次のようになります。

    protected void Page_Load(object sender, EventArgs e)
    {
        if (HttpContext.Current.User.IsInRole("canEdit"))
        {
            adminLink.Visible = true;
        }
    }
    

ページが読み込まれると、コードが実行されて、ログインしているユーザーが "canEdit" のロールを持っているかどうかが確認されます。 ユーザーが "canEdit" ロールに属している場合は、AdminPage.aspx ページへのリンクを含む span 要素が (結果的にはその span 内のリンクも) 表示されます。

製品管理の有効化

ここまでで、"canEdit" ロールを作成して、"canEditUser" ユーザー、管理フォルダー、管理ページを追加しました。 管理フォルダーとページへのアクセス権を設定して、"canEdit" ロールのユーザーのためにナビゲーション リンクをアプリケーションに追加しました。 次に、"canEdit" ロールを持つユーザーが製品を追加および削除できるように、AdminPage.aspx ページにマークアップを追加し、AdminPage.aspx.cs 分離コード ファイルにコードを追加します。

  1. ソリューション エクスプローラーで、Admin フォルダーから AdminPage.aspx ファイルを開きます。

  2. 既存のマークアップを次に置き換えます。

    <%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="AdminPage.aspx.cs" Inherits="WingtipToys.Admin.AdminPage" %>
    <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
        <h1>Administration</h1>
        <hr />
        <h3>Add Product:</h3>
        <table>
            <tr>
                <td><asp:Label ID="LabelAddCategory" runat="server">Category:</asp:Label></td>
                <td>
                    <asp:DropDownList ID="DropDownAddCategory" runat="server" 
                        ItemType="WingtipToys.Models.Category" 
                        SelectMethod="GetCategories" DataTextField="CategoryName" 
                        DataValueField="CategoryID" >
                    </asp:DropDownList>
                </td>
            </tr>
            <tr>
                <td><asp:Label ID="LabelAddName" runat="server">Name:</asp:Label></td>
                <td>
                    <asp:TextBox ID="AddProductName" runat="server"></asp:TextBox>
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" Text="* Product name required." ControlToValidate="AddProductName" SetFocusOnError="true" Display="Dynamic"></asp:RequiredFieldValidator>
                </td>
            </tr>
            <tr>
                <td><asp:Label ID="LabelAddDescription" runat="server">Description:</asp:Label></td>
                <td>
                    <asp:TextBox ID="AddProductDescription" runat="server"></asp:TextBox>
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server" Text="* Description required." ControlToValidate="AddProductDescription" SetFocusOnError="true" Display="Dynamic"></asp:RequiredFieldValidator>
                </td>
            </tr>
            <tr>
                <td><asp:Label ID="LabelAddPrice" runat="server">Price:</asp:Label></td>
                <td>
                    <asp:TextBox ID="AddProductPrice" runat="server"></asp:TextBox>
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator3" runat="server" Text="* Price required." ControlToValidate="AddProductPrice" SetFocusOnError="true" Display="Dynamic"></asp:RequiredFieldValidator>
                    <asp:RegularExpressionValidator ID="RegularExpressionValidator1" runat="server" Text="* Must be a valid price without $." ControlToValidate="AddProductPrice" SetFocusOnError="True" Display="Dynamic" ValidationExpression="^[0-9]*(\.)?[0-9]?[0-9]?$"></asp:RegularExpressionValidator>
                </td>
            </tr>
            <tr>
                <td><asp:Label ID="LabelAddImageFile" runat="server">Image File:</asp:Label></td>
                <td>
                    <asp:FileUpload ID="ProductImage" runat="server" />
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator4" runat="server" Text="* Image path required." ControlToValidate="ProductImage" SetFocusOnError="true" Display="Dynamic"></asp:RequiredFieldValidator>
                </td>
            </tr>
        </table>
        <p></p>
        <p></p>
        <asp:Button ID="AddProductButton" runat="server" Text="Add Product" OnClick="AddProductButton_Click"  CausesValidation="true"/>
        <asp:Label ID="LabelAddStatus" runat="server" Text=""></asp:Label>
        <p></p>
        <h3>Remove Product:</h3>
        <table>
            <tr>
                <td><asp:Label ID="LabelRemoveProduct" runat="server">Product:</asp:Label></td>
                <td><asp:DropDownList ID="DropDownRemoveProduct" runat="server" ItemType="WingtipToys.Models.Product" 
                        SelectMethod="GetProducts" AppendDataBoundItems="true" 
                        DataTextField="ProductName" DataValueField="ProductID" >
                    </asp:DropDownList>
                </td>
            </tr>
        </table>
        <p></p>
        <asp:Button ID="RemoveProductButton" runat="server" Text="Remove Product" OnClick="RemoveProductButton_Click" CausesValidation="false"/>
        <asp:Label ID="LabelRemoveStatus" runat="server" Text=""></asp:Label>
    </asp:Content>
    
  3. 次に、AdminPage.aspx を右クリックし、[コードの表示] をクリックして、AdminPage.aspx.cs 分離コード ファイルを開きます。

  4. AdminPage.aspx.cs 分離コード ファイル内の既存のコードを次のコードに置き換えます。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using WingtipToys.Models;
    using WingtipToys.Logic;
    
    namespace WingtipToys.Admin
    {
      public partial class AdminPage : System.Web.UI.Page
      {
        protected void Page_Load(object sender, EventArgs e)
        {
          string productAction = Request.QueryString["ProductAction"];
          if (productAction == "add")
          {
            LabelAddStatus.Text = "Product added!";
          }
    
          if (productAction == "remove")
          {
            LabelRemoveStatus.Text = "Product removed!";
          }
        }
    
        protected void AddProductButton_Click(object sender, EventArgs e)
        {
          Boolean fileOK = false;
          String path = Server.MapPath("~/Catalog/Images/");
          if (ProductImage.HasFile)
          {
            String fileExtension = System.IO.Path.GetExtension(ProductImage.FileName).ToLower();
            String[] allowedExtensions = { ".gif", ".png", ".jpeg", ".jpg" };
            for (int i = 0; i < allowedExtensions.Length; i++)
            {
              if (fileExtension == allowedExtensions[i])
              {
                fileOK = true;
              }
            }
          }
    
          if (fileOK)
          {
            try
            {
              // Save to Images folder.
              ProductImage.PostedFile.SaveAs(path + ProductImage.FileName);
              // Save to Images/Thumbs folder.
              ProductImage.PostedFile.SaveAs(path + "Thumbs/" + ProductImage.FileName);
            }
            catch (Exception ex)
            {
              LabelAddStatus.Text = ex.Message;
            }
    
            // Add product data to DB.
            AddProducts products = new AddProducts();
            bool addSuccess = products.AddProduct(AddProductName.Text, AddProductDescription.Text,
                AddProductPrice.Text, DropDownAddCategory.SelectedValue, ProductImage.FileName);
            if (addSuccess)
            {
              // Reload the page.
              string pageUrl = Request.Url.AbsoluteUri.Substring(0, Request.Url.AbsoluteUri.Count() - Request.Url.Query.Count());
              Response.Redirect(pageUrl + "?ProductAction=add");
            }
            else
            {
              LabelAddStatus.Text = "Unable to add new product to database.";
            }
          }
          else
          {
            LabelAddStatus.Text = "Unable to accept file type.";
          }
        }
    
        public IQueryable GetCategories()
        {
          var _db = new WingtipToys.Models.ProductContext();
          IQueryable query = _db.Categories;
          return query;
        }
    
        public IQueryable GetProducts()
        {
          var _db = new WingtipToys.Models.ProductContext();
          IQueryable query = _db.Products;
          return query;
        }
    
        protected void RemoveProductButton_Click(object sender, EventArgs e)
        {
          using (var _db = new WingtipToys.Models.ProductContext())
          {
            int productId = Convert.ToInt16(DropDownRemoveProduct.SelectedValue);
            var myItem = (from c in _db.Products where c.ProductID == productId select c).FirstOrDefault();
            if (myItem != null)
            {
              _db.Products.Remove(myItem);
              _db.SaveChanges();
    
              // Reload the page.
              string pageUrl = Request.Url.AbsoluteUri.Substring(0, Request.Url.AbsoluteUri.Count() - Request.Url.Query.Count());
              Response.Redirect(pageUrl + "?ProductAction=remove");
            }
            else
            {
              LabelRemoveStatus.Text = "Unable to locate product.";
            }
          }
        }
      }
    }
    

AdminPage.aspx.cs 分離コード ファイルに入力したコードでは、AddProducts というクラスによって、データベースに製品を追加する実際の作業が行われます。 このクラスはまだ存在しないため、ここで作成します。

  1. ソリューション エクスプローラーLogic フォルダーを右クリックして、[追加] ->[新しい項目] を選択します。
    [新しい項目の追加] ダイアログ ボックスが表示されます。

  2. 左側の [Visual C#] ->Code テンプレート グループを選択します。 次に、中央のリストから [クラス] を選択して、"ExceptionUtility.cs" という名前を付けます。
    新しいクラス ファイルが表示されます。

  3. 既存のコードを次のコードに置き換えます。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using WingtipToys.Models;
    
    namespace WingtipToys.Logic
    {
        public class AddProducts
        {
            public bool AddProduct(string ProductName, string ProductDesc, string ProductPrice, string ProductCategory, string ProductImagePath)
            {
                var myProduct = new Product();
                myProduct.ProductName = ProductName;
                myProduct.Description = ProductDesc;
                myProduct.UnitPrice = Convert.ToDouble(ProductPrice);
                myProduct.ImagePath = ProductImagePath;
                myProduct.CategoryID = Convert.ToInt32(ProductCategory);
    
                using (ProductContext _db = new ProductContext())
                {
                    // Add product to DB.
                    _db.Products.Add(myProduct);
                    _db.SaveChanges();
                }
                // Success.
                return true;
            }
        }
    }
    

AdminPage.aspx ページでは、"canEdit" ロールに属しているユーザーが製品の追加や削除を行うことができます。 新しい製品が追加されると、製品に関する詳細が検証されて、データベースに入力されます。 すぐにその新しい製品は、Web アプリケーションのすべてのユーザーに対して入手可能になります。

控えめな検証

AdminPage.aspx ページ上でユーザーが指定する製品の詳細は、検証コントロール (RequiredFieldValidatorRegularExpressionValidator) を使って検証されます。 これらのコントロールでは、控えめな検証が自動的に行われます。 控えめな検証を行うと、検証コントロールでクライアント側の検証ロジックに JavaScript を使うことができます。つまり、このページでは、サーバーへの移動を検証する必要がありません。 既定では、次の構成設定に基づいて、控えめな検証が Web.config ファイルに含まれます。

<add key="ValidationSettings:UnobtrusiveValidationMode" value="WebForms" />

[正規表現]

AdminPage.aspx ページの製品価格は、RegularExpressionValidator コントロールを使って検証されます。 このコントロールを使うと、関連付けられている入力コントロール ("AddProductPrice" TextBox) の値が正規表現で指定されたパターンと一致しているかどうかが検証されます。 正規表現は、特定の文字パターンをすばやく検索して照合できるパターン マッチング表記です。 RegularExpressionValidator コントロールには、ValidationExpression という名前のプロパティが含まれています。このプロパティには、次のように、価格入力の検証に使われる正規表現が含まれています。

<asp:RegularExpressionValidator 
    ID="RegularExpressionValidator1" runat="server"
    Text="* Must be a valid price without $." ControlToValidate="AddProductPrice" 
    SetFocusOnError="True" Display="Dynamic" 
    ValidationExpression="^[0-9]*(\.)?[0-9]?[0-9]?$">
</asp:RegularExpressionValidator>

FileUpload コントロール

入力コントロールと検証コントロールに加え、AdminPage.aspx ページに FileUpload コントロールを追加しました。 このコントロールには、ファイルをアップロードする機能があります。 この場合、画像ファイルのアップロードのみを許可します。 分離コード ファイル (AdminPage.aspx.cs) で AddProductButton をクリックすると、コードが実行されて FileUpload コントロールの HasFile プロパティが確認されます。 コントロールにファイルがあり、ファイルの種類 (ファイル拡張子に基づく) が許可されている場合、画像は Images フォルダーと、アプリケーションの Images/Thumbs フォルダーに保存されます。

モデル バインド

このチュートリアル シリーズの前半では、モデル バインドを使って ListView コントロール、FormsView コントロール、GridView コントロール、DetailView コントロールを設定しました。 このチュートリアルでは、モデル バインドを使って、DropDownList コントロールに製品カテゴリのリストを設定します。

AdminPage.aspx ファイルに追加したマークアップには、DropDownAddCategory という DropDownList コントロールが含まれています。

<asp:DropDownList ID="DropDownAddCategory" runat="server" 
        ItemType="WingtipToys.Models.Category" 
        SelectMethod="GetCategories" DataTextField="CategoryName" 
        DataValueField="CategoryID" >
    </asp:DropDownList>

モデル バインドを使ってこの DropDownList を設定するには、ItemType 属性と SelectMethod 属性を設定します。 ItemType 属性では、コントロールを設定するときに WingtipToys.Models.Category 型を使うように指定します。 このチュートリアル シリーズの冒頭で、Category クラスを作成して、この型を定義しました (以下を参照)。 Category クラスは、Models フォルダーの Category.cs ファイル内にあります。

public class Category
{
    [ScaffoldColumn(false)]
    public int CategoryID { get; set; }

    [Required, StringLength(100), Display(Name = "Name")]
    public string CategoryName { get; set; }

    [Display(Name = "Product Description")]
    public string Description { get; set; }

    public virtual ICollection<Product> Products { get; set; }
}

DropDownList コントロールの SelectMethod 属性では、分離コード ファイル (AdminPage.aspx.cs) に含まれている GetCategories メソッド (以下を参照) を使うように指定します。

public IQueryable GetCategories()
{
  var _db = new WingtipToys.Models.ProductContext();
  IQueryable query = _db.Categories;
  return query;
}

このメソッドでは、IQueryable インターフェイスを使って Category 型に対するクエリを評価するように指定します。 戻り値は、ページ (AdminPage.aspx) のマークアップに DropDownList を設定するために使います。

リスト内の各項目に表示されるテキストは、DataTextField 属性を設定して指定します。 DataTextField 属性では、Category クラスの CategoryName (上に示しています) を使って DropDownList コントロールの各カテゴリを表示します。 項目が DropDownList コントロール内で選択されているときに渡される実際の値は、DataValueField 属性に基づいています。 DataValueField 属性は、Category クラスに定義されているように、CategoryID に設定されます (上に示しています)。

アプリケーションのしくみ

"canEdit" ロールに属しているユーザーがページに初めて移動するときに、DropDownAddCategoryDropDownList コントロールが前述のように設定されます。 また、DropDownRemoveProductDropDownList コントロールにも、同じアプローチを使って製品を設定します。 "canEdit" ロールに属しているユーザーは、カテゴリの種類を選択し、製品の詳細 (名前説明価格画像ファイル) を追加します。 "canEdit" ロールに属しているユーザーが [製品の追加] ボタンをクリックすると、AddProductButton_Click イベント ハンドラーがトリガーされます。 分離コード ファイル (AdminPage.aspx.cs) 内にある AddProductButton_Click イベント ハンドラーによって、画像ファイルと許可されているファイルの種類 (.gif.png.jpeg.jpg) が一致していることが確認されます。 次に、この画像ファイルが Wingtip Toys サンプル アプリケーションのフォルダーに保存されます。 次に、新しい製品がデータベースに追加されます。 新しい製品を追加するために、AddProducts クラスの新しいインスタンスが作成され、製品に名前が付けられます。 AddProducts クラスには AddProduct という名前のメソッドがあり、products オブジェクトはこのメソッドを呼び出してデータベースに製品を追加します。

// Add product data to DB.
AddProducts products = new AddProducts();
bool addSuccess = products.AddProduct(AddProductName.Text, AddProductDescription.Text,
    AddProductPrice.Text, DropDownAddCategory.SelectedValue, ProductImage.FileName);

コードが実行されて新しい製品がデータベースに正常に追加されると、クエリ文字列値 ProductAction=add でページが再読み込みされます。

Response.Redirect(pageUrl + "?ProductAction=add");

ページが再読み込みされると、クエリ文字列が URL に含まれます。 ページを再読み込みすると、"canEdit" ロールに属しているユーザーは、AdminPage.aspx ページで DropDownList コントロールの更新をすぐに確認できます。 また、URL にクエリ文字列を含めると、このページで、"canEdit" ロールに属しているユーザーに成功メッセージを表示できます。

AdminPage.aspx ページが再読み込みされると、Page_Load イベントが呼び出されます。

protected void Page_Load(object sender, EventArgs e)
{
    string productAction = Request.QueryString["ProductAction"];
    if (productAction == "add")
    {
        LabelAddStatus.Text = "Product added!";
    }

    if (productAction == "remove")
    {
        LabelRemoveStatus.Text = "Product removed!";
    }
}

Page_Load イベント ハンドラーによって、クエリ文字列値の確認と、成功メッセージが表示するかどうかの判断が行われます。

アプリケーションの実行

ここで、アプリケーションを実行して、ショッピング カート内のアイテムを追加、削除、更新する方法を確認できます。 ショッピング カートの合計には、ショッピング カート内のすべてのアイテムの合計コストが反映されます。

  1. ソリューション エクスプローラーで、F5 キーを押して Wingtip Toys サンプル アプリケーションを実行します。
    ブラウザーが開き、Default.aspx ページが表示されます。

  2. ページの上部にある [ログイン] リンクをクリックします。

    Membership and Administration - Log In Link

    Login.aspx ページが表示されます。

  3. [ユーザー] 名と [パスワード] を入力します。

    Membership and Administration - Log In Page

  4. ページの下部付近にある [ログイン] ボタンをクリックします。

  5. ページの上部で、[管理者] リンクを選択して、AdminPage.aspx ページに移動します。

    Membership and Administration - Admin Link

  6. 入力検証をテストするには、製品の詳細は追加せずに [製品の追加] ボタンをクリックします。

    Membership and Administration - Admin Page

    必須フィールドのメッセージが表示されていることに注意してください。

  7. 新しい製品の詳細を追加して、[製品の追加] ボタンをクリックします。

    Membership and Administration - Add Product

  8. 上部のナビゲーション メニューから [製品] を選択して、追加した新しい製品を表示します。

    Membership and Administration - Show New Product

  9. [管理者] リンクをクリックして、管理ページに戻ります。

  10. ページの [製品の削除] セクションで、DropDownListBox に追加した新しい製品を選択します。

  11. [製品の削除] ボタンをクリックして、アプリケーションから新しい製品を削除します。

    Membership and Administration - Remove Product

  12. 上部のナビゲーション メニューから [製品] を選択して、製品が削除されていることを確認します。

  13. [ログオフ] をクリックして、管理モードを終了します。
    上部のナビゲーション ウィンドウに [管理者] が表示されなくなっていることに注意してください。

まとめ

このチュートリアルでは、カスタム ロールの追加とカスタム ロールに属しているユーザーの追加、管理フォルダーとページへのアクセスの制限、カスタム ロールに属しているユーザーへのナビゲーションの提供を行いました。 モデル バインドを使って、DropDownList コントロールにデータを設定しました。 FileUpload コントロールと検証コントロールを実装しました。 また、データベースでの製品の追加と削除の方法についても説明しました。 次のチュートリアルでは、ASP.NET ルーティングを実装する方法について説明します。

その他のリソース

Web.config - authorization 要素
ASP.NET Identity
メンバーシップ、OAuth、SQL Database を使った安全な ASP.NET Web Forms アプリを Azure Web サイトにデプロイする
Microsoft Azure - 無料試用版