This case study presents a Web application in which a user logs into a secure Web site to view a list of publications by an author of the user's choosing. The application consists of several ASPX files. Section 21.8.1 presents the working application and explains the purpose of each of its Web pages. Section 21.8.2 provides step-by-step instructions to guide you through building the application and presents the markup in the ASPX files as they are created.
21.8.1. Examining the Completed Secure Books Database Application
This example uses a technique known as forms authentication to protect a page so that only users known to the Web site can access it. Such users are known as the site's members. Authentication is a crucial tool for sites that allow only members to enter the site or a portion of the site. In this application, Web site visitors must log in before they are allowed to view the publications in the Books database. The first page that a user would typically request is Login.aspx (Fig. 21.42). You will soon learn to create this page using a Login control, one of several ASP.NET login controls that help create secure applications using authentication. These controls are found in the Login section of the Toolbox.
Figure 21.42. Login.aspx page of the secure books database application.
The Login.aspx page allows a site visitor to enter an existing user name and password to log into the Web site. A first-time visitor must click the link below the Log In button to create a new user before attempting to log in. Doing so redirects the visitor to CreateNewUser.aspx (Fig. 21.43), which contains a CreateUserWizard control that presents the visitor with a user registration form. We discuss the CreateUserWizard control in detail in Section 21.8.2. In Fig. 21.43, we use the password pa$$word for testing purposesas you will learn, the CreateUserWizard requires that the password contain special characters for security purposes. Clicking Create User establishes a new user account. Once the new user account is created, the user is automatically logged in and shown a success message (Fig. 21.44).
Figure 21.43. CreateNewUser.aspx page of the secure book database application.
Figure 21.44. Message displayed to indicate that a user account was created successfully.
Clicking the Continue button on the confirmation page sends the user to Books.aspx (Fig. 21.45), which provides a drop-down list of authors and a table containing the ISBNs, titles, edition numbers and copyright years of books in the database. By default, all the books by Harvey Deitel are displayed. Links appear at the bottom of the table that allow you to access additional pages of data. When the user chooses an author, a postback occurs, and the page is updated to display information about books written by the selected author (Fig. 21.46).
Figure 21.45. Books.aspx displaying books by Harvey Deitel (by default).
Figure 21.46. Books.aspx displaying books by Andrew Goldberg.
(This item is displayed on page 1125 in the print version)
Note that once the user creates an account and is logged in, Books.aspx displays a welcome message customized for the particular logged-in user. As you will soon see, a LoginName control provides this functionality. After you add this control to the page, ASP.NET handles the details of determining the user name.
Clicking the Click here to log out link logs the user out, then sends the user back to Login.aspx (Fig. 21.47). As you will learn, this link is created by a LoginStatus control, which handles the details of logging the user out of the page. To view the book listing again, the user must log in through Login.aspx. The Login control on this page receives the user name and password entered by a visitor. ASP.NET then compares these values with user names and passwords stored in a database on the server. If there is a match, the visitor is authenticated (i.e., the user's identity is confirmed). We explain the authentication process in detail in Section 21.8.2. When an existing user is successfully authenticated, Login.aspx redirects the user to Books.aspx (Fig. 21.45). If the user's login attempt fails, an appropriate error message is displayed (Fig. 21.48).
Figure 21.47. Logging in using the Login control.
Figure 21.48. Error message displayed for an unsuccessful login attempt using the Login control.
(This item is displayed on page 1126 in the print version)
Notice that Login.aspx, CreateNewUser.aspx and Books.aspx share the same page header containing the Bug2Bug logo image. Instead of placing this image at the top of each page, we use a master page to achieve this. As we demonstrate shortly, a master page defines common GUI elements that are inherited by each page in a set of content pages. Just as C# classes can inherit instance variables and methods from existing classes, content pages inherit elements from master pagesthis is known as visual inheritance.
21.8.2. Creating the Secure Books Database Application
Now that you are familiar with how this application behaves, we demonstrate how to create it from scratch. Thanks to the rich set of login and data controls provided by ASP.NET, you will not have to write any code to create this application. In fact, the application does not contain any code-behind files. All of the functionality is specified through properties of controls, many of which are set through wizards and other visual programming tools. ASP.NET hides the details of authenticating users against a database of user names and passwords, displaying appropriate success or error messages and redirecting the user to the correct page based on the authentication results. We now discuss the steps you must perform to create the secure books database application.
Step 1: Creating the Web Site
Create a new ASP.NET Web Site at http://localhost/Bug2Bug as described previously. We will explicitly create each of the ASPX files that we need in this application, so delete the IDE-generated Default.aspx file (and its corresponding code-behind file) by selecting Default.aspx in the Solution Explorer and pressing the Delete key. Click OK in the confirmation dialog to delete these files.
Step 2: Setting Up the Web Site's Folders
Before building any of the pages in the Web site, we create folders to organize its contents. First, create an Images folder and add the bug2bug.png file to it. This image can be found in the examples directory for this chapter. Next, add the Books.mdf database file (which can also be found in the examples directory) to the project's App_Data folder. We show how to retrieve data from this database later in the section.
Step 3: Configuring the Application's Security Settings
In this application, we want to ensure that only authenticated users are allowed to access Books.aspx (created in Step 9 and Step 10) to view the information in the database. Previously, we created all of our ASPX pages in the Web application's root directory (e.g., http://localhost/ProjectName). By default, any Web site visitor (regardless of whether the visitor is authenticated) can view pages in the root directory. ASP.NET allows you to restrict access to particular folders of a Web site. We do not want to restrict access to the root of the Web site, however, because all users must be able to view Login.aspx and CreateNewUser.aspx to log in and create user accounts, respectively. Thus, if we want to restrict access to Books.aspx, it must reside in a directory other than the root directory. Create a folder named Secure. Later in the section, we will create Books.aspx in this folder. First, let's enable forms authentication in our application and configure the Secure folder to restrict access to authenticated users only.
Select Website > ASP.NET Configuration to open the Web Site Administration Tool in a Web browser (Fig. 21.49). This tool allows you to configure various options that determine how your application behaves. Click either the Security link or the Security tab to open a Web page in which you can set security options (Fig. 21.50), such as the type of authentication the application should use. In the Users column, click Select authentication type. On the resulting page (Fig. 21.51), select the radio button next to From the internet to indicate that users will log in via a form on the Web site in which the user can enter a username and password (i.e., the application will use forms authentication). The default settingFrom a local networkrelies on users' Windows user names and passwords for authentication purposes. Click the Done button to save this change.
Figure 21.49. Web Site Administration Tool for configuring a Web application.
Figure 21.50. Security page of the Web Site Administration Tool.
(This item is displayed on page 1128 in the print version)
Figure 21.51. Choosing the type of authentication used by an ASP.NET Web application.
(This item is displayed on page 1128 in the print version)
Now that forms authentication is enabled, the Users column on the main page of the Web Site Administration Tool (Fig. 21.52) provides links to create and manage users. As you saw in Section 21.8.1, our application provides the CreateNewUser.aspx page in which users can create their own accounts. Thus, while it is possible to create users through the Web Site Administration Tool, we do not do so here.
Figure 21.52. Main page of the Web Site Administration Tool after enabling forms authentication.
(This item is displayed on page 1129 in the print version)
Even though no users exist at the moment, we configure the Secure folder to grant access only to authenticated users (i.e., deny access to all unauthenticated users). Click the Create access rules link in the Access Rules column of the Web Site Administration Tool (Fig. 21.52) to view the Add New Access Rule page (Fig. 21.53). This page is used to create an access rulea rule that grants or denies access to a particular Web application directory for a specific user or group of users. Click the Secure directory in the left column of the page to identify the directory to which our access rule applies. In the middle column, select the radio button marked Anonymous users to specify that the rule applies to users who have not been authenticated. Finally, select Deny in the right column, labeled Permission, then click OK. This rule indicates that anonymous users (i.e., users who have not identified themselves by logging in) should be denied access to any pages in the Secure directory (e.g., Books.aspx). By default, anonymous users who attempt to load a page in the Secure directory are redirected to the Login.aspx page so that they can identify themselves. Note that because we did not set up any access rules for the Bug2Bug root directory, anonymous users may still access pages there (e.g., Login.aspx, CreateNewUser.aspx). We create these pages momentarily.
Figure 21.53. Add New Access Rule page used to configure directory access.
(This item is displayed on page 1130 in the print version)
Step 4: Examining the Auto-Generated Web.config Files
We have now configured the application to use forms authentication and created an access rule to ensure that only authenticated users can access the Secure folder. Before creating the Web site's content, we examine how the changes made through the Web Site Administration Tool appear in the IDE. Recall that Web.config is an XML file used for application configuration, such as enabling debugging or storing database connection strings. Visual Web Developer generates two Web.config files in response to our actions using the Web Site Administration Toolone in the application's root directory and one in the Secure folder. [Note: You may need to click the Refresh button in the Solution Explorer to see these files.] In an ASP.NET application, a page's configuration settings are determined by the current directory's Web.config file. The settings in this file take precedence over the settings in the root directory's Web.config file.
After setting the authentication type for the Web application, the IDE generates a Web.config file at http://localhost/Bug2Bug/Web.config, which contains an authentication element
This element appears in the root directory's Web.config file, so the setting applies to the entire Web site. The value "Forms" of the mode attribute specifies that we want to use forms authentication. Had we left the authentication type set to From a local network in the Web Site Administration Tool, the mode attribute would be set to "Windows". Note that "Forms" is the default mode in a Web.config file generated for another purpose, such as saving a connection string.
After creating the access rule for the Secure folder, the IDE generates a second Web.config file in that folder. This file contains an authorization element that indicates who is; and who is not, authorized to access this folder over the Web. In this application, we want to allow only authenticated users to access the contents of the Secure folder, so the authorization element appears as
Rather than grant permission to each individual authenticated user, we deny access to those who are not authenticated (i.e., those who have not logged in). The deny element inside the authorization element specifies the users to whom we wish to deny access. When the users attribute's value is set to "?", all anonymous (i.e., unauthenticated) users are denied access to the folder. Thus, an unauthenticated user will not be able to load http://localhost/Bug2Bug/Secure/Books.aspx. Instead, such a user will be redirected to the Login.aspx pagewhen a user is denied access to a part of a site, ASP.NET by default sends the user to a page named Login.aspx in the application's root directory.
Step 5: Creating a Master Page
Now that you have established the application's security settings, you can create the application's Web pages. We begin with the master page, which defines the elements we want to appear on each page. A master page is like a base class in a visual inheritance hierarchy, and content pages are like derived classes. The master page contains placeholders for custom content created in each content page. The content pages visually inherit the master page's content, then add content in place of the master page's placeholders.
For example, you might want to include a navigation bar (i.e., a series of buttons for navigating a Web site) on every page of a site. If the site encompasses a large number of pages, adding markup to create the navigation bar for each page can be time consuming. Moreover, if you subsequently modify the navigation bar, every page on the site that uses it must be updated. By creating a master page, you can specify the navigation bar markup in one file and have it appear on all the content pages, with only a few lines of markup. If the navigation bar changes, only the master page changesany content pages that use it are updated the next time the page is requested.
In this example, we want the Bug2Bug logo to appear as a header at the top of every page, so we will place an Image control in the master page. Each subsequent page we create will be a content page based on this master page and thus will include the header. To create a master page, right click the location of the Web site in the Solution Explorer and select Add New Item.... In the Add New Item dialog, select Master Page from the template list and specify Bug2Bug.master as the filename. Master pages have the filename extension .master and, like Web Forms, can optionally use a code-behind file to define additional functionality. In this example, we do not need to specify any code for the master page, so leave the box labeled Place code in a separate file unchecked. Click Add to create the page.
The IDE opens the master page in Source mode (Fig. 21.54) when the file is first created. [Note: We added a line break in the DOCTYPE element for presentation purposes.] The markup for a master page is almost identical to that of a Web Form. One difference is that a master page contains a Master directive (line 1 in Fig. 21.54), which specifies that this file defines a master page using the indicated Language for any code. Because we chose not to use a code-behind file, the master page also contains a script element (lines 68). Code that would usually be placed in a code-behind file can be placed in a script element.
Figure 21.54. Master page in Source mode.
However, we remove the script element from this page, because we do not need to write any additional code. After deleting this block of markup, set the title of the page to Bug2Bug. Finally, notice that the master page contains a ContentPlaceHolder control in lines 1718. This control serves as a placeholder for content that will be defined by a content page. You will see how to define content to replace the ContentPlaceHolder shortly.
At this point, you can edit the master page in Design mode (Fig. 21.55) as if it were an ASPX file. Notice that the ContentPlaceHolder control appears as a large rectangle with a gray bar indicating the control's type and ID. Using the Properties window, change the ID of this control to bodyContent.
Figure 21.55. Master page in Design mode.
To create a header in the master page that will appear at the top of each content page, we insert a table into the master page. Place the cursor to the left of the ContentPlaceHolder and select Layout > Insert Table. In the Insert Table dialog, click the Template radio button, then select Header from the drop-down list of available table templates. Click OK to create a table that fills the page and contains two rows. Drag and drop the ContentPlaceHolder into the bottom table cell. Change the valign property of this cell to top, so the ContentPlaceHolder vertically aligns with the top of the cell. Next, set the Height of the top table cell to 130. Add to this cell an Image control named headerImage with its ImageUrl property set to the bug2bug.png file in the project's Images folder. (You can also simply drag the image from the Solution Explorer into the top cell.) Figure 21.56 shows the markup and Design view of the completed master page. As you will see in Step 6, a content page based on this master page displays the logo image defined here, as well as the content designed for that specific page (in place of the ContentPlaceHolder).
Figure 21.56. Bug2Bug.master page that defines a logo image header for all pages in the secure book database application.
1 <%-- Fig. 21.56: Bug2Bug.master --%> 2 <%-- Master page that defines common features of all pages in the --%> 3 <%-- secure book database application. --%> 4 <%@ Master Language="C#" %> 5 6 "-//W3C//DTD XHTML 1.1//EN" 7 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> 8 9
|"http://www.w3.org/1999/xhtml" > 10||"server"> 11||Bug2Bug 12 13||14 "form1" runat="server"> 15 16 "0"cellpadding="0"cellspacing="0"17style="width: 100%; height: 100%">18192324253031
Step 6: Creating a Content Page
We now create a content page based on Bug2Bug.master. We begin by building CreateNewUser.aspx. To create this file, right click the master page in the Solution Explorer and select Add Content Page. This action causes a Default.aspx file, configured to use the master page, to be added to the project. Rename this file CreateNewUser.aspx, then open it in Source mode (Fig. 21.57). Note that this file contains a Page directive with a Language property, a MasterPageFile property and a Title property. The Page directive indicates the MasterPageFile on which the content page builds. In this case, the MasterPageFile property is set to "~/Bug2Bug.master" to indicate that the current file builds on the master page we just created. The Title property specifies the title that will be displayed in the Web browser's title bar when the content page is loaded. This value, which we set to Create a New User, replaces the value (i.e., Bug2Bug) set in the title element of the master page.
Figure 21.57. Content page CreateNewUser.aspx in Source mode.
Because CreateNewUser.aspx's Page directive specifies Bug2Bug.master as the page's MasterPageFile, the content page implicitly contains the contents of the master page, such as the DOCTYPE, html and body elements. The content page file does not duplicate the XHTML elements found in the master page. Instead, the content page contains a Content control (lines 46 in Fig. 21.57), in which we will place page-specific content that will replace the master page's ContentPlaceHolder when the content page is requested. The ContentPlaceHolderID property of the Content control identifies the ContentPlaceHolder in the master page that the control should replacein this case, bodyContent.
The relationship between a content page and its master page is more evident in Design mode (Fig. 21.58). The shaded region contains the contents of the master page Bug2Bug.master as they will appear in CreateNewUser.aspx when rendered in a Web browser. The only editable part of this page is the Content control, which appears in place of the master page's ContentPlaceHolder.
Figure 21.58. Content page CreateNewUser.aspx in Design mode.
Step 7: Adding a CreateUserWizard Control to a Content Page
Recall from Section 21.8.1 that CreateNewUser.aspx is the page in our Web site that allows first-time visitors to create user accounts. To provide this functionality, we use a CreateUserWizard control. Place the cursor inside the Content control in Design mode and double click CreateUserWizard in the Login section of the Toolbox to add it to the page at the current cursor position. You can also drag-and-drop the control onto the page. To change the CreateUserWizard's appearance, open the CreateUserWizard Tasks smart tag menu, and click Auto Format. Select the Professional color scheme.
As discussed previously, a CreateUserWizard provides a registration form that site visitors can use to create a user account. ASP.NET handles the details of creating a SQL Server database (named ASPNETDB.MDF and located in the App_Data folder) to store the user names, passwords and other account information of the application's users. ASP.NET also enforces a default set of requirements for filling out the form. Each field on the form is required, the password must contain at least seven characters, including at least one nonalphanumeric character, and the two passwords entered must match. The form also asks for a security question and answer that can be used to identify a user in case the account's password needs to be reset or recovered.
After the user fills in the form's fields and clicks the Create User button to submit the account information, ASP.NET verifies that all the form's requirements were fulfilled and attempts to create the user account. If an error occurs (e.g., the user name already exists), the CreateUserWizard displays a message below the form. If the account is created successfully, the form is replaced by a confirmation message and a button that allows the user to continue. You can view this confirmation message in Design mode by selecting Complete from the Step drop-down list in the CreateUserWizard Tasks smart tag menu.
When a user account is created, ASP.NET automatically logs the user into the site (we say more about the login process shortly). At this point, the user is authenticated and allowed to access the Secure folder. After we create Books.aspx later in this section, we set the CreateUserWizard's ContinueDestinationPageUrl property to ~/Secure/Books.aspx to indicate that the user should be redirected to Books.aspx after clicking the Continue button on the confirmation page.
Figure 21.59 presents the completed CreateNewUser.aspx file (reformatted for readability). Inside the Content control, the CreateUserWizard control is defined by the markup in lines 940. The start tag (lines 912) contains several properties that specify formatting styles for the control, as well as the ContinueDestinationPageUrl property, which you will set later in the chapter. Lines 1432 contain elements that define additional styles used to format specific parts of the control. Finally, lines 3439 specify the wizard's two stepsCreateUserWizardStep and CompleteWizardStepin a WizardSteps element. CreateUserWizardStep and CompleteWizardStep are classes that encapsulate the details of creating a user and issuing a confirmation message.
Figure 21.59. CreateNewUser.aspx content page that provides a user registration form.
1 <%-- Fig. 21.59: CreateNewUser.aspx --%> 2 <%-- Content page using a CreateUserWizard control to register users. --%> 3 <%@ Page Language="C#" MasterPageFile="~/Bug2Bug.master" 4 Title="Create a New User" %> 5 6 "Content1" ContentPlaceHolderID="bodyContent" 7 Runat="Server"> 8 9 "CreateUserWizard1" runat="server" 10 BackColor="#F7F6F3" BorderColor="#E6E2D8" BorderStyle="Solid" 11 BorderWidth="1px" Font-Names="Verdana" Font-Size="0.8em" 12 ContinueDestinationPageUrl="~/Secure/Books.aspx"> 13 14 "#5D7B9D" BorderWidth="0px" 15 Font-Size="0.9em" VerticalAlign="Top" />16 "0px" Font-Names="Verdana" 17 ForeColor="White" /> 18 "#FFFBFF" BorderColor="#CCCCCC" 19 BorderStyle="Solid" BorderWidth="1px" Font-Names="Verdana" 20 ForeColor="#284775" /> 21 "#5D7B9D" BorderStyle="Solid" 22 Font-Bold="True" Font-Size="0.9em" 23 ForeColor="White" HorizontalAlign="Left" /> 24 "#FFFBFF" BorderColor="#CCCCCC" 25 BorderStyle="Solid" BorderWidth="1px" Font-Names="Verdana" 26 ForeColor="#284775" /> 27 "#FFFBFF" BorderColor="#CCCCCC" 28 BorderStyle="Solid" BorderWidth="1px" Font-Names="Verdana" 29 ForeColor="#284775" /> 30 "0px" /> 31 "#5D7B9D" Font-Bold="True" 32 ForeColor="White" /> 33 34 35 "server"> 36 37 "server"> 38 39 40 41
The sample outputs in Fig. 21.59(a) and Fig. 21.59(b) demonstrate successfully creating a user account with CreateNewUser.aspx. We use the password pa$$word for testing purposes. This password satisfies the minimum length and special character requirement imposed by ASP.NET, but in a real application, you should use a password that is more difficult for someone to guess. Figure 21.59(c) illustrates the error message that appears when you attempt to create a second user account with the same user nameASP.NET requires that each user name be unique.
Step 8: Creating a Login Page
Recall from Section 21.8.1 that Login.aspx is the page in our Web site that allows returning visitors to log into their user accounts. To create this functionality, add another content page named Login.aspx and set its title to Login. In Design mode, drag a Login control (located in the Login section of the Toolbox) to the page's Content control. Open the Auto Format dialog from the Login Tasks smart tag menu and set the control's color scheme to Professional.
Next, configure the Login control to display a link to the page for creating new users. Set the Login control's CreateUserUrl property to CreateNewUser.aspx by clicking the ellipsis button to the right of this property and selecting the CreateNewUser.aspx file in the resulting dialog. Then set the CreateUserText property to Click here to create a newuser. These property values cause a link to appear in the Login control.
Finally, we change the value of the Login control's DisplayRememberMe property to False. By default, the control displays a checkbox and the text Remember me next time. This can be used to allow a user to remain authenticated beyond a single browser session on the user's current computer. However, we want to require that users log in each time they visit the site, so we disable this option.
The Login control encapsulates the details of logging a user into a Web application (i.e., authenticating a user). When a user enters a user name and password, then clicks the Log In button, ASP.NET determines whether the information provided match those of an account in the membership database (i.e., ASPNETDB.MDF created by ASP.NET). If they match, the user is authenticated (i.e., the user's identity is confirmed), and the browser is redirected to the page specified by the Login control's DestinationPageUrl property. We set this property to the Books.aspx page after creating it in the next section. If the user's identity cannot be confirmed (i.e., the user is not authenticated), the Login control displays an error message (see Fig. 21.60), and the user can attempt to log in again.
Figure 21.60. Login.aspx content page using a Login control.
1 <%-- Fig. 21.60: Login.aspx --%> 2 <%-- Content page using a Login control that authenticates users. --%> 3 <%@ Page Language="C#" MasterPageFile="~/Bug2Bug.master" Title="Login" %> 4 5 "Content1" ContentPlaceHolderID="bodyContent" 6 Runat="Server"> 7 8 "Login1" runat="server" BackColor="#F7F6F3" 9 BorderColor="#E6E2D8" BorderPadding="4" BorderStyle="Solid" 10 BorderWidth="1px" CreateUserText="Click here to create a new user" 11 CreateUserUrl="~/CreateNewUser.aspx" 12 DestinationPageUrl="~/Secure/Books.aspx" DisplayRememberMe="False" 13 Font-Names="Verdana" Font-Size="0.8em" ForeColor="#333333"> 14 15 "#FFFBFF" BorderColor="#CCCCCC" 16 BorderStyle="Solid" BorderWidth="1px" Font-Names="Verdana" 17 Font-Size="0.8em" ForeColor="#284775" /> 18 "0.8em" /> 19 "#5D7B9D" Font-Bold="True" 20 Font-Size="0.9em" ForeColor="White" /> 21 "True" ForeColor="Black" /> 22 23
Figure 21.60 presents the completed Login.aspx file. Note that, as in CreateNewUser.aspx, the Page directive indicates that this content page inherits content from Bug2Bug.master. In the Content control that replaces the master page's ContentPlaceHolder with ID bodyContent, lines 822 create a Login control. Note the CreateUserText and CreateUserUrl properties (lines 1011) that we set using the Properties window. Line 12 in the start tag for the Login control contains the DestinationPageUrl (you will set this property in the next step) and the DisplayRememberMe property, which we set to False. The elements in lines 1521 define various formatting styles applied to parts of the control. Note that all of the functionality related to actually logging the user in or displaying error messages is completely hidden from you.
When a user enters the user name and password of an existing user account, ASP.NET authenticates the user and writes to the client an encrypted cookie containing information about the authenticated user. Encrypted data is data translated into a code that only the sender and receiver can understandthereby keeping it private. The encrypted cookie contains a string user name and a bool value that specifies whether this cookie should persist (i.e., remain on the client's computer) beyond the current session. Our application authenticates the user only for the current session.
Step 9: Creating a Content Page That Only Authenticated Users Can Access
A user who has been authenticated will be redirected to Books.aspx. We now create the Books.aspx file in the Secure folderthe folder for which we set an access rule denying access to anonymous users. If an unauthenticated user requests this file, the user will be redirected to Login.aspx. From there, the user can either log in or a create a new account, both of which will authenticate the user, thus allowing the user to return to Books.aspx.
To create Books.aspx, right click the Secure folder in the Solution Explorer and select Add New Item.... In the resulting dialog, select Web Form and specify the file name Books.aspx. Check the box Select Master Page to indicate that this Web Form should be created as a content page that references a master page, then click Add. In the Select a Master Page dialog, select Bug2Bug.master and click OK. The IDE creates the file and opens it in Source mode. Change the Title property of the Page directive to Book Information.
Step 10: Customizing the Secure Page
To customize the Books.aspx page for a particular user, we add a welcome message containing a LoginName control, which displays the current authenticated user name. Open Books.aspx in Design mode. In the Content control, type Welcome followed by a comma and a space. Then drag a LoginName control from the Toolbox onto the page. When this page executes on the server, the text [UserName] that appears in this control in Design mode will be replaced by the current user name. In Source mode, type an exclamation point (!) directly after the LoginName control (with no spaces in between). [Note: If you add the exclamation point in Design mode, the IDE may insert extra spaces or a line break between this character and the preceding control. Entering the ! in Source mode ensures that it appears adjacent to the user's name.]
Next, we add a LoginStatus control, which will allow the user to log out of the Web site when finished viewing the listing of books in the database. A LoginStatus control renders on a Web page in one of two waysby default, if the user is not authenticated, the control displays a hyperlink with the text Login; if the user is authenticated, the control displays a hyperlink with the text Logout. Each link performs the stated action. Add a LoginStatus control to the page by dragging it from the Toolbox onto the page. In this example, any user who reaches this page must already be authenticated, so the control will always render as a Logout link. The LoginStatus Tasks smart tag menu allows you switch between the control's Views. Select the Logged In view to see the Logout link. To change the actual text of this link, modify the control's LogoutText property to Click here to log out. Next, set the LogoutAction property to RedirectToLoginPage.
Step 11: Connecting the CreateUserWizard and Login Controls to the Secure Page
Now that we have created Books.aspx, we can specify that this is the page to which the CreateUserWizard and Login controls redirect users after they are authenticated. Open CreateNewUser.aspx in Design mode and set the CreateUserWizard control's ContinueDestinationPageUrl property to Books.aspx. Next, open Login.aspx and select Books.aspx as the DestinationPageUrl of the Login control.
At this point, you can run the Web application by selecting Debug > Start Without Debugging. First, create a user account on CreateNewUser.aspx, then notice how the LoginName and LoginStatus controls appear on Books.aspx. Next, log out of the site and log back in using Login.aspx.
Step 12: Generating a DataSet Based on the Books.mdf Database
We now begin to add the content (i.e., book information) to the secure page Books.aspx. This page will provide a DropDownList containing authors' names and a GridView displaying information about books written by the author selected in the DropDownList. A user will select an author from the DropDownList to cause the GridView to display information about only the books written by the selected author. As you will see, we create this functionality entirely in Design mode without writing any code.
To work with the Books database, we use an approach slightly different than in the preceding case study in which we accessed the Guestbook database using a SqlDataSource control. Here we use an ObjectDataSource control, which encapsulates an object that provides access to a data source. Recall that in Chapter 20, we accessed the Books database in a Windows application using TableAdapters configured to communicate with the database file. These TableAdapters placed a cached copy of the database's data in a DataSet, which the application then accessed. We use a similar approach in this example. An ObjectDataSource can encapsulate a TableAdapter and use its methods to access the data in the database. This helps separate the data-access logic from the presentation logic. As you will see shortly, the SQL statements used to retrieve data do not appear in the ASPX page when using an ObjectDataSource.
The first step in accessing data using an ObjectDataSource is to create a DataSet that contains the data from the Books database required by the application. In Visual C# 2005 Express, this occurs automatically when you add a data source to a project. In Visual Web Developer, however, you must explicitly generate the DataSet. Right click the project's location in the Solution Explorer and select Add New Item.... In the resulting dialog, select DataSet and specify BooksDataSet.xsd as the file name, then click Add. A dialog will appear that asks you whether the DataSet should be placed in an App_Code foldera folder whose contents are compiled and made available to all parts of the project. Click Yes for the IDE to create this folder to store BooksDataSet.xsd.
Step 13: Creating and Configuring an AuthorsTableAdapter
Once the DataSet is added, the Dataset Designer will appear, and the TableAdapter Configuration Wizard will open. Recall from Chapter 20 that this wizard allows you to configure a TableAdapter for filling a DataTable in a DataSet with data from a database. The Books.aspx page requires two sets of dataa list of authors that will be displayed in the page's DropDownList (created shortly) and a list of books written by a specific author. We focus on the first set of data herethe authors. Thus, we use the TableAdapter Configuration Wizard first to configure an AuthorsTableAdapter. In the next step, we will configure a TitlesTableAdapter.
In the TableAdapter Configuration Wizard, select Books.mdf from the drop-down list. Then click Next > twice to save the connection string in the application's Web.config file and move to the Choose a Command Type screen.
In the wizard's Choose a Command Type screen, select Use SQL statements and click Next >. The next screen allows you to enter a SELECT statement for retrieving data from the database, which will then be placed in an Authors DataTable within the BooksDataSet. Enter the SQL statement
SELECT AuthorID, FirstName + ' ' + LastName AS Name FROM Authors
in the text box on the Enter a SQL Statement screen. This query selects the AuthorID of each row. This query's result will also contain a column named Name that is created by concatenating each row's FirstName and LastName, separated by a space. The AS SQL keyword allows you to generate a column in a query resultcalled an aliasthat contains the result of a SQL expression (e.g., FirstName + ' ' + LastName). You will soon see how we use the result of this query to populate the DropDownList with items containing the authors' full names.
After entering the SQL statement, click the Advanced Options... button and uncheck Generate Insert, Update and Delete statements, since this application does not need to modify the database's contents. Click OK to close the Advanced Options dialog. Click Next > to advance to the Choose Methods to Generate screen. Leave the default names and click Finish. Notice that the DataSet Designer (Fig. 21.61) now displays a DataTable named Authors with AuthorID and Name members, and Fill and GeTData methods.
Figure 21.61. Authors DataTable in the Dataset Designer.
Step 14: Creating and Configuring a TitlesTableAdapter
Books.aspx needs to access a list of books by a specific author and a list of authors. Thus we must create a TitlesTableAdapter that will retrieve the desired information from the database's Titles table. Right click the Dataset Designer and from the menu that appears, select Add > TableAdapter... to launch the TableAdapter Configuration Wizard. Make sure the BooksConnectionString is selected as the connection in the wizard's first screen, then click Next >. Choose Use SQL statements and click Next >.
In the Enter a SQL Statement screen, open the Advanced Options dialog and uncheck Generate Insert, Update and Delete statements, then click OK. Our application allows users to filter the books displayed by the author's name, so we need to build a query that takes an AuthorID as a parameter and returns the rows in the Titles table for books written by that author. To build this complex query, click the Query Builder... button.
In the Add Table dialog that appears, select AuthorISBN and click Add. Then Add the Titles table, too. Our query will require access to data in both of these tables. Click Close to exit the Add Table dialog. In the top pane of the Query Builder window (Fig. 21.62), check the box marked * (All Columns) in the Titles table. Next, in the middle pane, add a row with Column set to AuthorISBN.AuthorID. Uncheck the Output box, because we do not want the AuthorID to appear in our query result. Add an @authorID parameter in the Filter column of the newly added row. The SQL statement generated by these actions retrieves information about all books written by the author specified by parameter @authorID. The statement first merges the data from the AuthorISBN and Titles tables. The INNER JOIN clause specifies that the ISBN columns of each table are compared to determine which rows are merged. The INNER JOIN results in a temporary table containing the columns of both tables. The outer portion of the SQL statement selects the book information from this temporary table for a specific author (i.e., all rows in which the AuthorID column is equal to @authorID).
Figure 21.62. Query Builder for designing a query that selects books written by a particular author.
Click OK to exit the Query Builder, then in the TableAdapter Configuration Wizard, click Next >. On the Choose Methods to Generate screen, enter FillByAuthorID and GetdataByAuthorID as the names of the two methods to be generated for the TitlesTableAdapter. Click Finish to exit the wizard. You should now see a Titles DataTable in the Dataset Designer (Fig. 21.63).
Figure 21.63. Dataset Designer after adding the TitlesTableAdapter.
Step 15: Adding a DropDownList Containing Authors' First and Last Names
Now that we have created a BooksDataSet and configured the necessary TableAdapters, we add controls to Books.aspx that will display the data on the Web page. We first add the DropDownList from which users can select an author. Open Books.aspx in Design mode, then add the text Author: and a DropDownList control named authorsDropDownList in the page's Content control, below the existing content. The DropDownList initially displays the text [Unbound]. We now bind the list to a data source, so the list displays the author information placed in the BooksDataSet by the AuthorsTableAdapter. In the DropDownList Tasks smart tag menu, click Choose Data Source... to start the Data Source Configuration Wizard. Select from the Select a data source dropdown list in the first screen of the wizard. Doing so opens the Choose a Data Source Type screen. Select Object and set the ID to authorsObjectDataSource, then click OK.
An ObjectDataSource accesses data through another object, often called a business object. Recall from Section 21.3 that the middle tier of a three-tier application contains business logic that controls the way an application's top tier user interface (in this case, Books.aspx) accesses the bottom tier's data (in this case, the Books.mdf database file). Thus, a business object represents the middle tier of an application and mediates interactions between the other two tiers. In an ASP.NET Web application, a TableAdapter typically serves as the business object that retrieves the data from the bottom-tier database and makes it available to the top-tier user interface through a DataSet. In the Choose a Business Object screen of the Configure Data Source wizard (Fig. 21.64), select BooksDataSetTableAdapters.AuthorsTableAdapter. [Note: You may need to save the project to see the AuthorsTableAdapter.] BooksDataSetTableAdapters is a namespace declared by the IDE when you create BooksDataSet. Click Next > to continue.
Figure 21.64. Choosing a business object for an ObjectDataSource.
(This item is displayed on page 1145 in the print version)
The Define Data Methods screen (Fig. 21.65) allows you to specify which method of the business object (in this case, AuthorsTableAdapter) should be used to obtain the data accessed through the ObjectDataSource. You can choose only methods that return data, so the only choice provided is the Getdata method, which returns an AuthorsDataTable. Click Finish to close the Configure Data Source wizard and return to the Data Source Configuration Wizard for the DropDownList (Fig. 21.66). The newly created data source (i.e., authorsObjectDataSource) should be selected in the top drop-down list. The other two drop-down lists on this screen allow you to configure how the DropDownList control uses the data from the data source. Set Name as the data field to display and AuthorID as the data field to use as the value. Thus, when authorsDropDownList is rendered in a Web browser, the list items will display the names of the authors, but the underlying values associated with each item will be the AuthorIDs of the authors. Finally, click OK to bind the DropDownList to the specified data.
Figure 21.65. Choosing a data method of a business object for use with an ObjectDataSource.
Figure 21.66. Choosing a data source for a DropDownList.
(This item is displayed on page 1146 in the print version)
The last step in configuring the DropDownList on Books.aspx is to set the control's AutoPostBack property to true. This property indicates that a postback occurs each time the user selects an item in the DropDownList. As you will see shortly, this causes the page's GridView (created in the next step) to display new data.
Step 16: Creating a GridView to Display the Selected Author's Books
We now add a GridView to Books.aspx for displaying the book information by the author selected in the authorsDropDownList. Add a GridView named titlesGridView below the other controls in the page's Content control.
To bind the GridView to data from the Books database, select from the Choose Data Source drop-down list in the GridView Tasks smart tag menu. When the Data Source Configuration Wizard opens, select Object and set the ID of the data source to titlesObjectDataSource, then click OK. In the Choose a Business Object screen, select the BooksDataSetTableAdapters.TitlesTableAdapter from the drop-down list to indicate the object that will be used to access the data. Click Next >. In the Define Data Methods screen, leave the default selection of GetdataByAuthorID as the method that will be invoked to obtain the data for display in the GridView. Click Next >.
Recall that TitlesTableAdapter method GeTDataByAuthorID requires a parameter to indicate the AuthorID for which data should be retrieved. The Define Parameters screen (Fig. 21.67) allows you to specify where to obtain the value of the @authorID parameter in the SQL statement executed by GeTDataByAuthorID. Select Control from the Parameter source drop-down list. Select authorsDropDownList as the ControlID (i.e., the ID of the parameter source control). Next, enter 1 as the DefaultValue, so books by Harvey Deitel (who has AuthorID 1 in the database) display when the page first loads (i.e., before the user has made any selections using the authorsDropDownList). Finally, click Finish to exit the wizard. The GridView is now configured to display the data retrieved by TitlesTableAdapter.GetDataByAuthorID, using the value of the current selection in authorsDropDownList as the parameter. Thus, when the user selects a new author and a postback occurs, the GridView displays a new set of data.
Figure 21.67. Choosing the data source for a parameter in a business object's data method.
(This item is displayed on page 1147 in the print version)
Now that the GridView is tied to a data source, we modify several of the control's properties to adjust its appearance and behavior. Set the GridView's CellPadding property to 5, set the BackColor of the AlternatingRowStyle to LightYellow, and set the BackColor of the HeaderStyle to LightGreen. Change the Width of the control to 600px to accommodate long data values.
Next, in the GridView Tasks smart tag menu, check Enable Sorting. This causes the column headings in the GridView to turn into hyperlinks that allow users to sort the data in the GridView. For example, clicking the Titles heading in the Web browser will cause the displayed data to appear sorted in alphabetical order. Clicking this heading a second time will cause the data to be sorted in reverse alphabetical order. ASP.NET hides from you the details required to achieve this functionality.
Finally, in the GridView Tasks smart tag menu, check Enable Paging. This causes the GridView to split across multiple pages. The user can click the numbered links at the bottom of the GridView control to display a different page of data. GridView's PageSize property determines the number of entries per page. Set the PageSize property to 4 using the Properties window so that the GridView displays only four books per page. This technique for displaying data makes the site more readable and enables pages to load more quickly (because less data is displayed at one time). Note that, as with sorting data in a GridView, you do not need to add any code to achieve paging functionality. Fig. 21.68 displays the completed Books.aspx file in Design mode.
Figure 21.68. Completed Books.aspx in Design mode.
(This item is displayed on page 1148 in the print version)
Step 17: Examining the Markup in Books.aspx
Figure 21.69 presents the markup in Books.aspx (reformatted for readability). Aside from the exclamation point in line 9, which we added manually in Source mode, all the remaining markup was generated by the IDE in response to the actions we performed in Design mode. The Content control (lines 655) defines page-specific content that will replace the ContentPlaceHolder named bodyContent. Recall that this control is located in the master page specified in line 3. Line 9 creates the LoginName control, which displays the authenticated user's name when the page is requested and viewed in a browser. Lines 1012 create the LoginStatus control. Recall that this control is configured to redirect the user to the login page after logging out (i.e., clicking the hyperlink with the LogoutText).
Figure 21.69. Markup for the completed Books.aspx file.
(This item is displayed on pages 1149 - 1151 in the print version)
1 <%-- Fig. 21.69: Books.aspx --%> 2 <%-- Displays information from the Books database. --%> 3 <%@ Page Language="C#" MasterPageFile="~/Bug2Bug.master" 4 Title="Book Information" %> 5 6 "Content1" ContentPlaceHolderID="bodyContent" 7 Runat="Server"> 8 9 Welcome, "LoginName1" runat="server" />! 10 "LoginStatus1" runat="server" 11 LogoutAction="RedirectToLoginPage" 12 LogoutText="Click here to log out" /> 13 14 Author: 15 "authorsDropDownList" runat="server" 16 AutoPostBack="True" DataSourceID="authorsObjectDataSource" 17 DataTextField="Name" DataValueField="AuthorID"> 18 19 20 "authorsObjectDataSource" 21 runat="server" SelectMethod="GetData" 22 TypeName="BooksDataSetTableAdapters.AuthorsTableAdapter"> 23 24 25 "titlesGridView" runat="server" AllowPaging="True" 26 AllowSorting="True" AutoGenerateColumns="False" CellPadding="5" 27 DataKeyNames="ISBN" DataSourceID="titlesObjectDataSource" 28 PageSize="4" Width="600px"> 29 30 31 "ISBN" 32 HeaderText="ISBN" ReadOnly="True" SortExpression="ISBN" /> 33 "Title" 34 HeaderText="Title" SortExpression="Title" /> 35 "EditionNumber" 36 HeaderText="EditionNumber" SortExpression="EditionNumber" /> 37 "Copyright" 38 HeaderText="Copyright" SortExpression="Copyright" /> 39 40 41 "LightGreen" /> 42 "LightYellow" /> 43 44 45 "titlesObjectDataSource" runat="server" 46 SelectMethod="GetDataByAuthorID" 47 TypeName="BooksDataSetTableAdapters.TitlesTableAdapter"> 48 49 50 "authorsDropDownList" 51 DefaultValue="1" Name="authorID" 52 PropertyName="SelectedValue" Type="Int32" />53 54 55
Lines 1518 define the DropDownList that displays the names of the authors in the Books database. Line 16 contains the control's AutoPostBack property, which indicates that changing the selected item in the list causes a postback to occur. The DataSourceID property in line 16 specifies that the DropDownList's items are created based on the data obtained through the authorsObjectDataSource (defined in lines 2023). Line 21 specifies that this ObjectDataSource accesses the Books database by calling method GeTData of the BooksDataSet's AuthorsTableAdapter (line 22).
Lines 2543 create the GridView that displays information about the books written by the selected author. The start tag (lines 2528) indicates that paging (with a page size of 4) and sorting are enabled in the GridView. The AutoGenerateColumns property indicates whether the columns in the GridView are generated at runtime based on the fields in the data source. This property is set to False, because the IDE-generated Columns element (lines 3039) already specifies the columns for the GridView using BoundFields. Lines 4554 define the ObjectDataSource used to fill the GridView with data. Recall that we configured titlesObjectDataSource to use method GetdataByAuthorID of the BooksDataSet's TitlesTableAdapter for this purpose. The ControlParameter in lines 5052 specifies that the value of method GetdataByAuthorID's parameter comes from the SelectedValue property of the authorsDropDownList.
Figure 21.69(a) depicts the default appearance of Books.aspx in a Web browser. Because the DefaultValue property (line 51) of the ControlParameter for the titlesObjectDataSource is set to 1, books by the author with AuthorID 1 (i.e., Harvey Deitel) are displayed when the page first loads. Note that the GridView displays paging links below the data, because the number of rows of data returned by GeTDataByAuthorID is greater than the page size. Figure 21.69(b) shows the GridView after clicking the 2 link to view the second page of data. Figure 21.69(c) presents Books.aspx after the user selects a different author from the authorsDropDownList. The data fits on one page, so the GridView does not display paging links.
Introduction to Computers, the Internet and Visual C#
Introduction to the Visual C# 2005 Express Edition IDE
Introduction to C# Applications
Introduction to Classes and Objects
Control Statements: Part 1
Control Statements: Part 2
Methods: A Deeper Look
Classes and Objects: A Deeper Look
Object-Oriented Programming: Inheritance
Polymorphism, Interfaces & Operator Overloading
Graphical User Interface Concepts: Part 1
Graphical User Interface Concepts: Part 2
Strings, Characters and Regular Expressions
Graphics and Multimedia
Files and Streams
Extensible Markup Language (XML)
Database, SQL and ADO.NET
ASP.NET 2.0, Web Forms and Web Controls
Networking: Streams-Based Sockets and Datagrams
Searching and Sorting
Appendix A. Operator Precedence Chart
Appendix B. Number Systems
Appendix C. Using the Visual Studio 2005 Debugger
Appendix D. ASCII Character Set
Appendix E. Unicode®
Appendix F. Introduction to XHTML: Part 1
Appendix G. Introduction to XHTML: Part 2
Appendix H. HTML/XHTML Special Characters
Appendix I. HTML/XHTML Colors
Appendix J. ATM Case Study Code
Appendix K. UML 2: Additional Diagram Types
Appendix L. Simple Types