This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.
|
Add a Touch of Web Class to Your Server
Jason Masterman |
While Active Server Pages are revolutionary, they can be difficult to develop and debug. WebClasses let you apply your knowledge of Visual Basic to hardcore Web development. |
If you do any Web development at all, you should use WebClasses. WebClasses in Visual Basic® 6.0 are the most exciting new development frameworks around.
Here, I'll start with a detailed look at the architecture involved to utilize server-side components and WebClasses. Next, I'll cover the various ways to enhance server-side script by calling components that run on the server. I'll explain what technologies are available outside of WebClasses and why WebClasses have evolved to solve a lot of problems those other technologies don't. Next, I'll explore some problems associated with techniques that don't use the new WebClass technology. For example, many Web applications mix presentation code and data access code in a single Active Server Page (ASP) file. WebClasses provide one of the cleanest architectures for separating the UI code, which belongs in the HTML template, from the business and data access code that belongs in your components. The next step is to look at the issues involved with maintaining state in a Web application and why state really matters. To understand why state matters, you first needs to look at how a user transitions from page to page in a Web application. You need to ensure that clients access your Web pages only when the client request is in the appropriate state. Since Web browsers make it simple to bookmark a page, you need to implement safeguards to ensure that users don't bypass the application sequence and jump to an internal page while their session isn't in an appropriate state. To demonstrate WebClass technology, I will walk you through the creation of a Web application that accesses data from Pubs, the sample database that comes with Microsoft® SQL Server. To follow along with me, you'll need to have the appropriate software installed. If you use Windows® 98, make sure you've installed Personal Web Server. If you use Windows NT® 4.0, you need to have installed the Windows NT 4.0 Option Pack as well as the Windows NT 4.0 Service Pack 4 so that you can get all the cool new features working in Visual Basic 6.0. The Architecture So exactly what is a WebClass, anyway? It's simply a component that's called from an ASP page. The WebClass component lives on the Web server and provides the developer with a means of responding to requests from Web clients. To create a WebClass, you first need to start a new Microsoft Internet Information Server (IIS) application with Visual Basic 6.0 (see Figure 1). |
Figure 1: Starting a New Project |
Once you've created a new project, you can double click on the WebClass designer that Visual Basic created. It should be called WebClass1 (see Figure 2).
Figure 2: WebClass1 |
If you run your application at this point, Visual Basic will prompt you to create a temporary virtual root directory where the application will reside on your Web server. By default, this virtual directory will be your \Windows\Temp directory. When Visual Basic creates the ASP file for your WebClass, it will place
Figure 3: Creating a Virtual Root |
Visual Basic automatically adds some default code to your WebClass's Start event so that you'll see something the first time you run it. Before I show you the code in the WebClass, let's take a look at the ASP file that the WebClass generated for you. You will notice the URL for your Web application is http://localhost/temp/WebClass1.asp. If you let Visual Basic set your directory to \Windows\Temp, you can find the actual file in \Windows\Temp\WebClass1.asp. The name of the ASP file is determined by the NameInURL property value of the WebClass. Each WebClass designer in a project gets its own ASP file.
Figure 4: Running WebClass1 |
Let's examine the code in the ASP file (see Figure 5). The first interesting line creates the WebClassRuntime. WebClassManager object and stores it in the ASP Application variable ~WC~WebClassManager:
|
The WebClassManager lives in a file called mswcrun.dll. When you created the IIS application, Visual Basic automatically set a reference to this library for the application. The Visual Basic Object Browser will show the WebClassManager class listed under the WebClassLibrary, but only if you select the "Show Hidden Members" option in the object browser (see Figure 6). The mswcrun.dll file serves as a runtime entry point into your WebClass and will execute code inside it. The WebClass runtime stands behind the server and the ASP page that calls it. |
Figure 6: Showing the WebClassManager |
The next interesting piece of code in the ASP file is the call to the ProcessNoStateWebClass method on the WebClassManager. Note how the name includes the words NoState. When I discuss how to maintain state in a WebClass, you'll see this call changed to the ProcessRetainInstanceWebClass method. The parameters passed into the ProcessNoStateWebClass method call include all the standard ASP objects as well as the ProgID for your WebClass component, Project1.WebClass1 in this example.
The only real purpose of the ASP page is to serve as the entry point into the WebClass. This is the only file a client needs to call to access the WebClass. But for your WebClass to respond to more direct requests from the client, the client has to submit some more information with the ASP file. Visual Basic does this by appending this information in the URL itself, after the ASP file name.
The Life of a WebClass
Now let's look at the life of a WebClass, what events are called, and in what order they're called. The WebClass supports several key events. Understanding the order in which these events occur will help you better understand the life of your objects. Since your object is currently set to not maintain state, the event sequence will always be the following:
In Visual Basic, take a look at the Start event for the WebClass. The code is generating a page. This is just some simple stub code provided by Microsoft. Since you don't want this code in the final release of your project, you can go ahead and delete it. Now you can create your own WebItems to display the information you want shown.
There are two different types of WebItemsHTML template WebItems and custom WebItems. HTML template WebItems are just HTML files that are imported into a WebClass. They're a great way to send back information to the browser. With template WebItems, you get the ability to completely separate presentation code from any custom methods, data access code, or calls to other business objects you may want to use from the WebClass. You can design pages with graphics, links, tables, and whatever other elements you want without having to dirty up presentation code with existing WebClass or component code. Mixing presentation code and data access code in ASP files has been one of the biggest problems faced by beginner Web developers who are using ASP. Later, I'll show you how to use template WebItems that allow you to inject content into them using special tags before sending the template back to the client.
Custom WebItems are quite different from template WebItems. You'd typically use custom WebItems when you want to generate an HTML page from thin air and send it back to the client. If you use custom WebItems, you'll have to put some presentation code into your WebClass to create the HTML page that gets sent back to the browser. If you ever want to modify The appearance of this HTML, you'll need to modify the WebClass code. I prefer to avoid this by using templates in most of my code. However, I can also show you a good example of when and how to use custom WebItems. Keep in mind that both template WebItems and custom WebItems have one goal: to send HTML back to the browser.
Figure 7 shows the code for a very simple HTML page that contains some text, a link, a graphic, and a button. Your WebClass can display this page when it's started. To do this, you need to make the HTML file part of your project. In the WebClass designer, select "Add HTML Template WebItem" and pull in the file. If you haven't already saved your project, Visual Basic will prompt you to do so when you import the HTML file. This is because Visual Basic creates a separate copy of the HTML file you've referenced and places it in your project directory. If the file already exists in the directory, Visual Basic will append a number to the file name. After the HTML file is added to your project as a template WebItem, you'll need to give it a name (like FirstPage). After you save these changes, your designer should look like Figure 8. Now you can provide the necessary code to display the template WebItem when your application starts, by adding code to events.
In the following code, the NextItem property is used to pass control to another WebItem within the WebClass:
|
When the flow control is passed on, the Respond event for the associated WebItem is called. You can write the HTML template to the browser by calling the WebItem's WriteTemplate method. What you've done is use the WebClass's Start event to pass program control to the FirstPage WebItem, where the HTML file is written out to the client.
Now that the client can see the first page, you need to connect items on that page with other HTML template WebItems or custom WebItems in your WebClass. Figure 8 shows the designer with the HTML template item selected. You will notice two body items listed in the right pane: listAuthors and Hlink. When the template was imported, the designer parsed the HTML file looking for any tags that could be used from the Web browser to submit requests for information. Any items that have been given an ID attribute in DHTML will have their ID displayed. Any nonIDed items will be displayed with a temporary id consisting of the element type. Each of these items can then be connected to events in the WebClass. You can use the Hlink item to reference another HTML template WebItem and the ListAuthors button to reference a custom WebItem. The button, when pressed, will make a call to the custom WebItem, which will then call another component to request all the authors from the SQL Server Pubs database. The next step is to import the SecondPage template WebItem (see Figure 9) and connect the FirstPage Hlink to it. To do this, I just right-clicked on the Hlink of the FirstPage template in the designer and selected Connect To WebItem. Notice the Target and HTML Context columns of the designer before and after connecting the WebItem. Before making the connections, the Target column entry is <None> and the HTML Context column contains an HREF tag with an empty target string. After connecting the SecondPage WebItem, the target contains SecondPage and the HTML context has changed dramatically. The tag now reads: |
|
The WebClass has modified the link so that when it's clicked it calls WebClass1.asp. The WCI=SecondPage item tells the WebClass which WebItem to call. The WCU item here passes no information, but can be used to pass other user data to the browser. If you specified a custom WebEvent, you'd also get a WCE item that would direct the WebClass to that custom event. (More on custom events later.) When the ASP page calls the SecondPage WebItem, it's handled just like FirstPagethe Respond event is fired, and you can call the WriteTemplate method within it.
Sample Custom WebItem Now let's create a custom WebItem that displays a table with all the authors from the Pubs database. When you display the authors table, it would be cool to provide a link for each author that, when selected, would query the database and display the titles for that particular author. The trick to displaying the titles is that you want to create one page that can display the titles for any author. This can be achieved through a generic page that inserts information about a particular author before sending the HTML page back to the browser. You should also ensure that no matter what link is clicked while viewing the author's page, you call into a single event handler for the WebClass. Make sure to add a link to this titles page that takes the user back to the authors page. The URLFor method automatically generates the correct URL for this link. First, create a custom WebItem called ShowAuthors by right-clicking the WebClass in the designer window. Once this WebItem is created, you need to connect the ListAuthors button to it. Next, you need to provide code in the WebItem's Respond event that dynamically creates the HTML page. The WebItem's Respond event will call a component running in Microsoft Transaction Server (MTS) that can query the database for the authors and return a recordset object. You can then use that recordset object to generate the HTML page. If you're at all familiar with MTS, you're probably asking a few questions. If the WebClass runs on the Web server, shouldn't that component be installed as an MTS component? Why call another component in MTS? The method that retrieves the authors just issues a simple select statement against the database; it doesn't perform any transactions. The component could just as easily run outside of MTS (which would certainly makes debugging easier); it neither requires nor makes use of the real benefits of MTS. WebClasses are more concerned with the presentation layer than with the business logic layer, and presentation layer code doesn't need to run inside MTS. WebClasses are compatible with IIS 3.0, so they don't provide direct support (such as properties) for running under MTS. I chose to install this component under MTS just to introduce the issue; the choice is up to you in this instance. To learn more about MTS and WebClasses, please see the Advanced Basics column in this issue of MIND. Figure 10 shows how to create HTML on the fly in the ShowAuthors Respond event. Since you want to display a table that contains a link for each author's name, but don't want to hardwire the URL, you can have the WebClass dynamically generate a URL for the link by calling its URLFor method. The author links will eventually call a generic template WebItem for displaying titles for the selected author. Since it's not yet determined where the template WebItem is going to be stored on disk, its value shouldn't be hardcoded. The URLFor method takes a WebItem as the first parameter and an optional eventname parameter as the second parameter. In this case, you can use the unique ID from the authors table (in the au_id field) as this eventname. The following WebClass code |
|
generates a URL that looks like this: |
|
The WCI parameter points to another WebItem (Titles) I haven't created yet. The WCE parameter indicates that the link will generate a call to the Titles_UserEvent routine,
and will pass in the author id (172-32-1176) as the eventname parameter.
When this event is called, you must perform two actions. First, the event code should run another query against the database, finding the titles for the author id that was passed in. Next, you should write the Titles template WebItem back to the browser. Before sending the template back to the browser, the selected author's title can be inserted into the outgoing HTML stream by performing a tag substitution. Figure 11 shows the code for the Titles template. This template performs tag substitution. When you create a template WebItem you can use it in a couple of different ways. You can use the template to simply write HTML back to the browser without performing any preprocessing. I've already introduced examples of this when I called the WriteTemplate method of the FirstPage WebItem earlier. The more powerful option is to perform some processing of the template WebItem before sending it back to the browser. This is called tag substitution. To perform tag substitution you must specify a tag prefix as a property of the template WebItem. The default tag prefix is wc@. When a tag with a prefix is encountered, the WebItem is able to resolve that particular tag to the correct data. You can see in Figure 11 that I've specified tags for the author id, title, and price. When called, the WebClass will look for tags that match the tagprefix property for the associated template. It will then fire the ProcessTag event for each tag it finds in that WebItem. The ProcessTag event is where you can provide the content you want to inject into the template before sending it back to the browser. Tag substitution can be recursive. If you set the ReScanReplacements property of the WebItem to true, the WebClass will process the replaced text for more tagged items. The author search query shown in Figure 12 is called within the Titles_UserEvent procedure. It passes the eventname the function received as a parameter to the TitleLookUp method of the MTS component. The query is another simple lookup (see Figure 13). It uses a module level recordset, rsTitles, so the recordset can be accessed from other events in the WebClass. After the UserEvent calls the TitleLookUp method, the property NextItem is set to Titles, causing the Titles_Respond event to fire. In the Respond event, the Titles.WriteTemplate method is called, which then starts the processing of the template and fires the ProcessTag event for each tag found in the template. |
|
This event receives a parameter, the name of the tag currently being processed. You simply provide a case statement to inject the appropriate code for that particular item.
Your application is now able to send plain template WebItems, dynamically generated HTML pages, and tag- substituted templates back to the browser. I have also covered several key events, methods, and properties of my WebClass and WebItems. You've seen how the employ the services of other components that run in MTS and perform database lookups. Adding a Login Page What hasn't been covered yet is any discussion of state management and controlling access to the Web page. The next step is to add a login page. Before users are granted access to other pages, the program can check to make sure that the user is logged in. To do this you'll need to track the user once they log in so that they can be granted access to the other pages. If a user's login is enforced before allowing them to access any other pages, what's really being controlled is the order in which a person gets to a particular state in the application. To do this you already know that you need to store some state information to indicate whether the user is logged in. And what would a discussion of WebClasses be if it did not include the hot topic of stateless versus persistent components? State matters here because you have to use the state to control the path the user takes through the application. If the execution sequence is not important, then state will not be very important either. The first thing to do is create a login page and connect it to the Start event of the WebClass. The login page will be called in place of the FirstPage template WebItem that was created earlier. Next, a custom event should be created to perform validation when a user submits the login form. Figure 14 shows the HTML for this login page. To create the custom event, first add the login page to the project, then right-click on the Login template WebItem and select Add Custom Event. The custom event should appear under the Login template WebItem. Then connect the login template's button to the custom event you just created. Your WebClass designer should now look like Figure 15. Modify the WebClass_Start event to call the WriteTemplate method of this new WebItem. When you run and test your application, the login form should be the first thing you see. When you click the login button, however, nothing will happen. Code for the login custom event is shown in Figure 16. Notice how a user name and password were hardcoded in for demonstration purposes. If the information submitted for the login is correct, a module-level variable named m_LoggedIn is set to true and the user is routed to the FirstPage WebItem via the URLFor method. If the login fails, the user is redirected to this same page. Before the login page is written back to the browser, a message is added using the tag replacement technique. |
|
Maintaining State If you run and test the application, the login form and custom event should perform as expected. They keep the user from getting into the system unless they log in first. Of course, this is only true so far if the user always starts with the login page. If they go directly to some page they've previously marked as a favorite, they can easily bypass the login page. One reason for this is that there are no checks in any of the other pages to ensure that the m_LoggedIn flag is set to true before writing them out. The app needs code added to all of the template WebItem Respond events to perform a simple check of the m_LoggedIn flag. Once these checks are added, the system should work fine. Well, not exactly. When a user first logs in to the application the m_LoggedIn flag is set to true. As soon as the page is finished processing this request and the EndRequest event is complete, the object goes out of scope and is destroyed after the Terminate event is called. This means that the state that's being stored in the m_LoggedIn flag disappears, and is reinitialized to false the next time the user makes a request. Obviously, this is not the behavior you want. The app needs to maintain state across requests from the client. There are a few different ways to approach this problem. The first requires changing a property on the WebClass. The StateManagement property is set to a default value of "1 - wcNoState". If you remember the beginning of this project, the ASP file for this WebClass included a method called ProcessNoStateWebClass. If the StateManagement property is set to "2 - wcRetainInstance", the ASP file is updated to call a new method, ProcessRetainInstanceWebClass. This causes the WebClass object to stay alive between requests from the client, changing the object's lifecycle. The new lifecycle of the WebClass will be an Initialize call, followed by one or more calls of BeginRequest, Start/WebItem event, EndRequest, ReleaseInstance, and Terminate. For this to work, a reference to the WebClass has to be stored in an ASP Session object. Storing a reference in the Session object creates a few problems. First, for sessions to work the browser making the request needs to support cookies. ASP uses cookies to store a unique ID about a particular user for that session. The cookie is then written back to the client browser. When the client makes subsequent requests, the cookie is sent back to the server to identify this particular client with its session. Most modern browsers support cookies, so this is not a big deal. However, when ASP stores a object reference to the WebClass in the Session object, it creates another side effect. ASP used a particular thread to create the WebClass. That thread was one of a pool of threads available to ASP. When the client calls back into the WebClass, ASP has to use the same thread that created the WebClass to access it. It's possible that another client request used the same thread to create their WebClass and even worse, that client could be in the middle of a request while your WebClass is submitting its own. This means that ASP won't be able to get to that thread to access your WebClass until the other client request is finished being processed. This is true even if ASP has other free threads in its thread pool. So whenever you create an apartment threaded object (the Visual Basic default) and store a reference to it in the ASP session, you're effectively pinning down that object to a particular thread. Not a good idea. What all this really means is that if you use the Session object to store a reference to an object, you are creating a system that does not allow for scalability. You're creating an area of contention for the specific thread your object was created with. Also, if you have any intention of load balancing by running a Web farm to handle requests from thousands of clients, you'll have to make sure that all client-side calls matched up with the server they were routed to the first time the request came into the system. Again, this does not make your system very easy to scale. Another way to store state is to put information inside the Session or Application objects. This does not require the WebClass to be set to wcRetainInstance. An example of using the application object was shown in the earlier WebClass ASP file. To store state in the Session object, simply use the following code: |
|
You can also send information back and forth to the browser by either writing out your own cookies, using hidden fields (as many people already do), or using the URLData property of the WebClass. The URLData method allows you to pass information from the browser to the server by appending it to the URL itself. The WCU parameter of the WebClass will contain the data the client passes back to you. Since it appends data to the URL string, the URLData property has some limitations. You can only send about 2KB worth of data, depending on the browser software the client is using. If you need to send more data, you should use cookies instead. If the client is using Microsoft Internet Explorer 4.x, you also need to use the POST method, not the GET method, if you want to get the URLData property to work. The Visual Basic documentation provides more information on this limitation.
Wrap-up WebClasses are one of the best new pieces of technology in Visual Basic 6.0. If you use them, you'll find your Web development efforts moving much more rapidly and easily. You will be able to easily walk through your Visual Basic code when it's called from the ASP file associated with your WebClass. You will also be able to use classes in your projects, unlike the current limitations of ASP scripting. Your code will be compiled, not interpreted, making it run faster. You won't have to mix presentation code with data access code and normal function processing. You can segregate your development efforts, letting people who are comfortable with graphics and HTML create templates for developers who are best at programming and using tools like Visual Basic. Most importantly, you won't pull your hair out trying to debug those darn ASP applications! Be sure and read more about how WebClasses and MTS interact in the Advanced Basics column by Ted Pattison in this same issue. |
From the April 1999 issue of Microsoft Internet Developer.