When we looked at client-side JavaScript, we saw that on its own it can't actually do that much; it's simply a programming language. It's only when it is used to manipulate the Browser Object Model (BOM) that we find out how powerful it can be. With server-side scripting, we have a new set of objects that we can manipulate to create the results we want. The good news is that the server-side object model available to ASP is much smaller and easier to learn than the client-side BOM.
In this section, we'll look at some of the ASP objects available, including the Request, Response, Server, Session, and Application objects. We'll see how they enable us to obtain information submitted to the page in a form, find out which browser the user has, and redirect users to different pages. We'll also see how we can keep track of users as they move around our website and how we can store information on the server.
Before we start looking at ASP objects in detail, we'll briefly look at two important concepts that, once understood, help explain why two of the ASP objects we'll see shortly exist and what their purposes are.
We talked earlier in the chapter about how browsing to a web page on the Internet or on a web server running on a local network is a two-stage process. First, the user requests a web page from a server by typing in the name of the server, the directory, and the file name of the web page to retrieve. The request might have also been made by clicking a link in a web page or by submitting a form in a page. The web server receives the request along with some additional information about the client computer, the browser, and any information contained inside a form if the request was a result of a form being submitted.
When the ASP object model was being designed by Microsoft, they had to decide on the name of the object that would represent a request and, yes, you guessed it, they decided to name it the Request object. Anything that has anything to do with an incoming request, such as form data being passed or even cookies being sent, can be accessed in our server-side JavaScript code using the Request object.
We know that the request is only half the equation: After we have the request, we need to deal with the response side of things. The page that was requested is automatically sent back to the requesting browser, so what is the programmer's part in the response? Well, we can set cookies that are sent back to the browser. We can change the HTML in the page before it's sent back, and we can even redirect the user so that a page different than the one requested is sent back.
With much imagination, it was decided that the ASP object to represent the response side of things would be called the Response object. Bet you didn't see that coming!
The Response and Request objects are like the BOM objects that a browser makes available in that we don't need to create them; they are created for us and are ready to use. The difference is that it's now the server that creates them for us, and not a browser. This applies to all of the ASP objects.
It's time now to look in much more detail at the Request and Response objects, and also some of the other objects that ASP makes available to us.
The Request object's purpose is to help us find out details about the last request made by the user for a page on our server.
When the user makes a request for a web page, for example by clicking on a link in a web page or typing a URL into the browser's location box, the details of the request are carried to the server via HyperText Transport Protocol (HTTP). This protocol simply specifies, at a low level, how the request should be made, what information should be passed to the server, and the way that the sent information must be structured. The HTTP request contains a header and possibly a body. The header contains information about the request, such as which browser the user has, which page (if any) made the request, what cookies there are for that server, and so on. The body contains information sent with the request, such as information entered in the form that posted the request. Luckily enough, we don't need to worry about these details or how the HTTP protocol works because using the Request object makes getting all this information very easy.
We'll start by looking at how we can get information submitted from a form.
In Chapter 6, we saw how to create a form in a web page and how we can access that information client-side, using the objects associated with the different form elements.
We briefly mentioned two attributes of the <form> tag, but we did not use them since they were only applicable to server-side script. The method attribute determines how the information is sent to the server. The most common value for this attribute is post, although sometimes get is used. For the moment, we'll concentrate on how to retrieve information that has been sent via the post method.
The other attribute of the <form> tag that is now relevant is the action attribute. This specifies the page to which the information in the form should be sent.
Let's look at an example. Say we have a form in the page MyFormPage.htm and that the form has just two text boxes and a submit button.
<form action="ProcessFormPage.asp" method=post name=form1> <input type="text" name="txtName"> <br> <input type="text" name="txtAge"> <br> <input type="submit" value="Post This Form" name=submit1> </form>
When the user clicks the Submit button, all the information in the form elements, that is, the values entered in the text boxes at the time of the form submission, will be sent to the server. Where to? In this case, our form will be submitted to the page ProcessFormPage.asp. We must specify an .asp page, otherwise we won't be able to do any server-side processing and make use of the form information. You can think of the form posting as a little like the user clicking a hyperlink, in that the user is directed to a new page. The main difference is that any form data is passed, along with the direction, to a new page.
After the form is submitted to the page specified in the action attribute of the <form> tag, we can read the values sent with the form. In our form, the page receiving the request will be the ProcessFormPage.asp and it's here that we can write the JavaScript to read and process the form information before sending the page ProcessFormPage.asp back to the user. The key to getting form information is the Form collection property of the Request object. A collection property is very similar to an array property, and is accessed in a similar way.
The page that contains the form being submitted will submit the form's content information, along with the name attribute value of each control that the information relates to. Note that it's the name attribute and not the ID attribute that's important here. In our example, our page contained a form with two text controls: one named txtName and the other named txtAge. To get that information we could write something like this in our ProcessFormPage.asp:
var postedName = Request.Form("txtName") var postedAge = Request.Form("txtAge")
This will result in our postedName and postedAge variables containing the values contained by txtName and txtAge when the form was submitted by the user.
In this simple example, we'll create a form that asks users for their first and last names, and then submits this information to another page, which displays it back to the users. We'll first create the page with a form on it, with just two text boxes and a submit button.
<html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <body> <form action="ReceiveSimpleForm.asp" method=post id=form1 name=form1> <P> First Name : <input id=txtFirstName name=txtFirstName> </P> <P> Last Name : <input id=txtLastName name=txtLastName> </P> <P> <input id=submit1 name=submit1 type=submit value="Post me to the server"> </P> </form> </body> </html>
Save it as SimpleForm.htm in the physical directory C:\MyWeb sites\MyExampleDirectory of the AWalkOnTheServerSide virtual directory.
Now create the ASP file that the form is to be posted to.
<%@ language = JavaScript%> <html> <head> <% var lastName = Request.Form("txtLastName"); %> </head> <body> <P>The value you entered for your first name was <%= Request.Form("txtFirstName") %></P> <P>The value you entered for your last name was <%= lastName %></P> </body> </html>
Save this as ReceiveSimpleForm.asp in the same directory as SimpleForm.htm.
We need to browse to the SimpleForm.htm page on our local server by using either our server name or localhost. For example, type the following address into the browser:
http://localhost/AWalkOnTheServerSide/SimpleForm.htm
Now enter some information in the text boxes, and click the Post me to the server button.
The page posted to our ReceiveSimpleForm.asp page displays the information we entered in the text boxes.
The SimpleForm.htm page is easy enough to follow; just a form containing two text boxes and a submit button.
<form action="ReceiveSimpleForm.asp" method=post id=form1 name=form1>
Notice that the form's definition specifies how the form is to be posted using the method attribute, set here as post. In addition, the action attribute specifies which page is to receive and handle the form data that is posted, which in our case is ReceiveSimpleForm.asp.
We'll now turn to the ReceiveSimpleForm.asp page. At the very top of the page, we've specified that the scripting language for that page is JavaScript.
<%@ language = JavaScript%>
In between the <head> tags, we have our first server-side JavaScript. We mark the start of the server-side script using <% and the end with %>. Inside our server-side script block we have just one line.
var lastName = Request.Form("txtLastName");
We declare a variable called lastName and set its value to the value of the form element txtLastName. The variable is declared outside of any function, so it will be global to this page and available to any server-side script later in the page. Remember, this is server-side script so the variable is available only during the server-side processing and will be lost once the page is received at the user's browser.
Then, in the body of the page, we get the information contained within the form element txtFirstName, again using the Request.Form collection property and the name of the element.
<P>The value you entered for your first name was <%= Request.Form("txtFirstName") %></P>
We write the information to the page by enclosing the script in <%= and %> tags; the equals sign after the opening tag causes any value returned by the script to be written to the page at that point.
Finally, we write the value of the variable lastName that we declared earlier to the page.
<P>The Value you entered for your last name was <%= lastName %></P>
Accessing the values of text boxes is quite easy, but what about controls like lists, drop-down list boxes, radio buttons, and finally the textarea element? Actually, all of these are accessed in the same way as we access the text box.
When we define select elements and radio buttons, we give each item in the select list or each radio button in a group a value attribute. The item in the select list, or radio button in the group that has been selected by the user will have its value sent to the server; all the values of the other select items and radio buttons are ignored.
For example, let's imagine that we have the following form elements in a form that has been posted to the server.
<input type="radio" name=radio1 value="FirstRadioButton"> <input type="radio" name=radio1 value="SecondRadioButton"> <input type="radio" name=radio1 value="ThirdRadioButton"> <select name=selYear size=1> <option value=1999>1999</option> <option value=2000>2000</option> <option value=2001>2001</option> </select>
If, when the form was submitted, the user had selected the second radio button and the third item in the select element, what would be contained in the following variables?
var radioValue = Request.Form("radio1"); var selectValue = Request.Form("selYear");
The answer is that the server-side variable radioValue would contain the string "SecondRadioButton" and the selectValue variable would contain the string "2001". Although client-side, the radio button group and the select option array contain more than one value. When they are posted to an ASP page and processed server-side, we get only the value of the radio button or option that the user selected prior to submitting the form.
With a checkbox, if it's checked when the form is submitted to the server, its value is sent to the server. If it's not checked, no value is sent to the server.
If the checkbox didn't have a value attribute when the checkbox was defined in the form, a checked checkbox will have "on" as its value.
You can define two or more checkboxes with the same name in a form, in which case the value of each checked checkbox will be passed. For example, imagine that this is our form:
<form action="test.asp" method=post id=form1 name=form1> <input type="checkbox" name=checkbox1 value="Hello"> <input type="checkbox" name=checkbox1 value="GoodBye"> <input type="submit" name=submit1 value="Submit"> </form>
If just the first checkbox were checked, the value of the checkboxValue variable would be just the string "Hello".
var checkboxValue = Request.Form("checkbox1");
If the user had checked both checkboxes, the value of checkboxValue variable would be "Hello, GoodBye".
The textarea form element sends its value just like a text box does, but with one important difference: It has multiple lines, carriage returns, and line feeds. When we looked at this control in Chapter 6, we mentioned the wrap attribute. This attribute has three possible values: off, soft, and hard. If the wrap attribute is off, which is the default if the attribute is not set, any text in the control will be on the same line, unless the user presses the return key. If the wrap attribute is set to soft, when the text reaches the end of the line, it will wrap automatically on to the next line, much as your word processor does. However, any new lines caused by this wrapping won't be sent along with the text when it is posted to the server. If wrap is set to hard, text will wrap at the end of a line and the new line characters will be sent along with the text when posted to the server.
Different operating systems may use different characters for new lines, as we saw in Chapter 6, so it's important to be aware of the new line characters for each operating system your visitors might have. You might be surprised at the differences! It's best to test your ASP pages with all the platforms that you expect your visitors to be using.
Let's create a more complex form. This time it's a suggestion form. The users enter their name and e-mail details, gender, age, whether they expect a response to their suggestion, and finally their suggestion. We do some basic validation of the form client-side and post the form only if it's actually filled in. If the form passes this validation, it is posted to another page, which displays the values to the user.
First let's create the form page.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <script language=JavaScript> function form1_onsubmit() { var returnValue = false; if (document.form1. txtName.value == "") { alert("Please enter your name"); document.form1.txtName.focus(); } else if (document.form1.txtEmail.value == "") { alert("Please enter your e-mail address"); document.form1.txtEmail.focus(); } else if (document.form1.txtaSuggestion.value == "") { alert("Please enter your suggestion"); document.form1.txtaSuggestion.focus(); } else { returnValue = true; } return returnValue; } </script> </head> <body> <form action="ReceiveComplexForm.asp" method=post name=form1 onsubmit="return form1_onsubmit()"> <P>Name : <input name=txtName></P> <P>E-mail : <input name=txtEmail></P> Gender : <br> Female <input type="radio" name=radGender value=Female> Male <input type="radio" name=radGender value=Male> <br><br> Your age range <select name=selAgeRange size=1> <option value=0_18 selected>Under 18</option> <option value=19_40>19 - 40</option> <option value=41_70>41 - 70</option> <option value=70+>70+</option> </select> <br><br> Tick if you want a response <input name=chkResponse type=checkbox value=SendResponse> <br><br> Your suggestion <br> <textarea cols=20 rows=10 name=txtaSuggestion wrap=hard></textarea> <br><br> <P> <input name=submit1 type=submit value="Send Suggestion"> </P> </form> </body> </html>
Save this as ComplexForm.htm in a directory that can be browsed to via your website, for example the physical directory of the AWalkOnTheServerSide virtual directory or your web server's wwwRoot directory.
Next, we have the page that receives the post from the form.
<%@ language = JavaScript%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <body> <P>Name is <%= Request.Form("txtName") %></P> <P>E-mail is <%= Request.Form("txtEmail") %></P> <P>Gender is <%= Request.Form("radGender") %></P> <P>Response is <%= Request.Form("chkResponse") %></P> <P>Age Range is <%= Request.Form("selAgeRange") %></P> <pre> <P>Suggestion is <%= Request.Form("txtaSuggestion") %></P> </pre> </body> </html>
Save this in the same directory as ComplexForm.htm with the name ReceiveComplexForm.asp.
Load the ComplexForm.htm page into a web browser, for example by typing the following:
http://localhost/AWalkOnTheServerSide/ComplexForm.htm
We'll see a form like the one in Figure 16-30, which you need to fill in. (I've filled in my details). Client-side script checks that you have entered your name, e-mail, and suggestion and won't let you submit the form unless you have.
With the form completed, click the Send Suggestion button and the form is posted to the ReceiveComplexForm.asp page, which displays the details that were entered in the submitted form. (See Figure 16-31.)
Try submitting the form with different values. In particular, see what happens when the checkbox is not checked as opposed when it is checked.
Within the body of ComplexForm.htm we have a form with the name form1. In addition to text boxes, the form also includes a radio button group, a select control, a checkbox, and a textarea. This allows us to demonstrate which information each control passes back to the server.
First, we have two text boxes for the name and e-mail address of the user.
<P>Name : <input name=txtName></P> <P>E-mail : <input name=txtEmail></P>
As we saw in the previous example, the value sent to the server for these controls is the value entered by the user.
For each control other than the text boxes and the textarea, we have a value specified by the VALUE attribute. The radio buttons have the values Female and Male.
Gender : <br> Female <input type="radio" checked name=radGender value=Female> Male <input type="radio" name=radGender value=Male>
Whichever radio button is selected when the form is posted is the one whose value will be submitted.
In the case of the select control, each option has its own value.
Your age range <select name=selAgeRange size=1> <option value=0_18 selected>Under 18</option> <option value=19_40>19 - 40</option> <option value=41_70>41 - 70</option> <option value=70+>70+</option> </select>
Again, whichever item is selected in the select control when the form is submitted will be the one whose value is posted.
The checkbox is also given a value attribute.
Tick if you want a response <input name=chkResponse type=checkbox value=SendResponse>
However, this differs from the preceding situations in that the value is posted only if the checkbox is checked, otherwise no value is posted to the server. If we want to discover whether the checkbox was checked, we simply need to see whether its value is SendResponse or whether it has no value.
Within the textarea control, we've set its wrap attribute to hard. That way when the user's text comes to the end of a line, it will wrap to the next, and this formatting will be passed to the server when the form is posted.
Your suggestion <br> <textarea cols=20 rows=10 name=txtaSuggestion wrap=hard></textarea>
Also, notice that we haven't left any spaces between the open and close <textarea> tags and that they are on the same line. This is because the value of the textarea is the string contained between its opening and closing tags. If we don't do this, then if the user does not enter any input into the textarea, the value of the textarea won't be an empty string, but a space or a new line character instead. This makes it more difficult to determine whether the user has entered a suggestion.
The last item in the form is a submit button for the form.
<input name=submit1 type=submit value="Send Suggestion">
In the simple form example we gave earlier in the chapter, we didn't do any form validation client-side before posting the form. We simply had a submit button, which when clicked automatically posted the form without the need for additional JavaScript.
This time however, when we define the <form>, we capture the Form object's onsubmit event handler and connect it to the form1_onsubmit() function, which we defined in the head of the page. Notice that the onsubmit attribute of the <form> tag returns the value from the function form1_onsubmit(). If false is returned, the form submission is canceled; if true is returned, the form submission goes ahead.
<form action="ReceiveComplexForm.asp" method=post name=form1 onsubmit="return form1_onsubmit()">
Also, note from the definition of the <form> that the method for submission of the form is post, and the form is to be submitted to the page ReceiveComplexForm.asp. We will look at this page shortly.
First we will take a closer look at the form1_onsubmit() function, defined in the head of the ComplexForm.htm page. This starts by defining the variable returnValue and initializing it to false.
function form1_onsubmit() { var returnValue = false;
The function then checks whether the text box with name txtName has a value. If it doesn't, the user is alerted to this fact, and the focus of the page is sent back to this control.
if (document.form1. txtName.value == "") { alert("Please enter your name"); document.form1.txtName.focus(); }
This check is then repeated for the text box controls called txtEmail, and the textarea control called txtaSuggestion.
else if (document.form1.txtEmail.value == "") { alert("Please enter your e-mail address"); document.form1.txtEmail.focus(); } else if (document.form1.txtaSuggestion.value == "") { alert("Please enter your suggestion"); document.form1.txtaSuggestion.focus(); }
Finally, if none of the conditions is true, meaning that everything we want filled in is filled in, the final else statement sets returnValue to true.
else { returnValue = true; }
The variable returnValue is returned at the end of the function. As explained previously, if it contains false, the form submission is canceled, otherwise it goes ahead as normal.
return returnValue; }
That's enough checks for our simple test website, but if our page were accessible from the Internet we'd need to thoroughly check that every form element exists and has a value to defeat evil hackers who try to crash our ASP page by sending it erroneous form data. We may also want to add similar form validation on the server side, because client-side JavaScript can be disabled and that disables all our form validation.
We'll now turn to the ReceiveComplexForm.asp page, to which the form in ComplexForm.htm is posted. Recall that now we're operating server-side, so if we want to use JavaScript we always need to specify the language for the page as JavaScript.
<%@ language = JavaScript%>
Then inside the body of the page we print out the values of the text boxes, the radio button, the checkbox, and the select list.
<P>Name is <%= Request.Form("txtName") %></P> <P>E-mail is <%= Request.Form("txtEmail") %></P> <P>Gender is <%= Request.Form("radGender") %></P> <P>Response is <%= Request.Form("chkResponse") %></P> <P>Age Range is <%= Request.Form("selAgeRange") %></P>
Finally, we've put the response from the textarea box inside <pre> tags. That way it will show the text in the box exactly as it's submitted, line breaks and all. If the text in the textarea box is on different lines, that will be reflected here:
If we change the textarea box's definition in the ComplexForm.htm page so that its wrap attribute is soft rather than hard, we'll find that all the text submitted will be on one line unless the user has explicitly put the text on another line by pressing return.
As well as passing values using a form submission, we can also attach values to the URL of the page we want the values to be sent to. You may have seen websites where, for example, the URLs for searches look something like the following:
http://www.someserver.com/search_web site.asp?SearchFor=SomeInfo&AndSomeOtherInfo= otherinfo
We can pass information like this from a web page to the server in various ways.
We can add the information to a hyperlink manually.
We can use script to navigate to a URL with data added to the end.
We can use a form, but change the method attribute to get rather than post.
Here, we'll just look at how to add data to the end of a URL using the first two methods, rather than using a form. The form post method is usually preferable to the form get method because it's easier to use and can handle much larger amounts of data. However, the form get does have the advantage that it can be added to the browser favorites by the user, making bookmarking a search possible. Bookmarking the results of a form post won't work because the data in the form post is not saved with the bookmarking.
We'll start by looking at how we build up a URL that has values attached. Let's imagine that we want to pass a name and someone's age via a URL.
First, we have the URL of the page that will receive the data. If it's on our local server, it might be something like this:
http://localhost/MyGetInfopage.asp
To add data to the end of the URL, we first need to tell the browser where the URL ends and the data starts. We do this using a question mark.
http://localhost/MyGetInfopage.asp?
Now, let's add the name data by first adding the key name for the data. This can be anything we like, but it must include only characters and numbers, not punctuation or spaces unless we escape them as we did with cookies. Let's use a key name of Name.
http://localhost/MyGetInfopage.asp?Name
Now we need to say what Name is equal to; that is, what data we are passing. To do this, we simply add an equals sign, then the data itself. In the following example, I'm passing Paul as the data:
http://localhost/MyGetInfopage.asp?Name=Paul
It's important to note that only characters and numbers can be passed like this in some browsers such as Netscape. To pass punctuation, spaces, or special characters, we need to take some extra steps; we need to encode them with their ASCII hexadecimal value, in front of which we put a % sign. (See Appendix D for details.) For example, for a space we should use %20. So, to pass the data Paul Wilton we need the following code:
http://localhost/MyGetInfopage.asp?Name=Paul%20Wilton
However, if we might be using JavaScript to navigate the page to a different location
window.location.href = "http://localhost/MyGetInfopage.asp?Name=Paul%20Wilton"
we can use the escape() function, which does any necessary encoding for us.
var myURLData = escape("Paul Wilton"); window.location.href = " http://localhost/MyGetInfopage.asp?Name=" + myURLData;
What do we do if we want to pass more than one item of data?
In this case, we simply need to separate the items of data with ampersands (&). For example, to pass a Name and Age we use the following:
http://localhost/MyGetInfopage.asp?Name=Paul&Age=31
As mentioned, the escape() function is only strictly necessary for browsers like Netscape, but we can use it with IE. It won't cause any problems, and saves checking for the browser type.
Now that we know how to send the data, how do we extract it using server-side script?
To extract the data from the ASP page that has been navigated to, we use the Request object's QueryString collection property. This contains all the data passed in the URL, which can be retrieved by name.
For example, to retrieve the Name and Age data sent in the previous URL, we would write the following request:
Request.QueryString("Name") Request.QueryString("Age")
In this example, we'll create a simple page that will pass information in a URL to another page, which will then display it. We illustrate two methods of attaching the data to the URL: by writing the URL as we have done previously, and by using script to add the data to the end of the URL.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <script language=JavaScript type="text/javascript"> function butNavigate_onclick() { var urlData = "Name=" + escape("Paul Wilton") + "&Age=31"; window.location.href = "GetInfo.asp?" + urlData; } </script> </head> <body> <A href="GetInfo.asp?Name=Paul%20Wilton&Age=31">Get Info</A> <form name=form1 id=form1> <input type="button" value="Send Data" name=butNavigate onclick="return butNavigate_onclick()"> </form> </body> </html>
Save this page as PostInfo.htm into the physical directory of the AWalkOnTheServerSide virtual directory on your web server.
Next, we have the page that extracts the data.
<%@ language = JavaScript%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <body> <p>The info posted by the URL was</p> Name : <%= Request.QueryString("Name") %> <br> Age : <%= Request.QueryString("Age") %> </body> </html>
Save this page as GetInfo.asp in the same directory as the PostInfo.htm file.
Browse to the PostInfo.htm page on our web server using the following URL:
http://localhost/AWalkOnTheServerSide/PostInfo.htm
We'll see a page with a link and a button, as shown in Figure 16-32.
Here we are giving the user two methods, either clicking the link or clicking the button, to send the data that we were looking at to the GetInfo.asp page. This data will be captured and displayed on that page, as shown in Figure 16-33.
In PostInfo.htm, the first method for sending the data to GetInfo.asp is through a hyperlink. The hyperlink has the data added to the end, as we've already seen.
<A href="GetInfo.asp?Name=Paul%20Wilton&Age=31">Get Info</A>
Note how we've manually replaced the space between Paul and Wilton with %20, the hexadecimal character equivalent.
The second method for sending the data is through clicking a button.
<input type="button" value="Send Data" name=butNavigate onclick="return butNavigate_onclick()">
The button's onclick event handler is connected to the butNavigate_onclick() function, which is contained in the head of the page.
function butNavigate_onclick() { var urlData = "Name=" + escape("Paul Wilton") + "&Age=31"; window.location.href = "GetInfo.asp?" + urlData; }
In this function, the variable urlData is initialized to the data string to be attached to the end of the URL. We need to use the escape() function to convert Paul Wilton into a form that can be sent with the URL. We don't need to escape the age data, because that is acceptable as it is. In addition, we mustn't escape the names of the data or the equals sign, because that must be sent as it is. The escape() function would convert the = to its hexadecimal character equivalent, which we don't want.
Finally, the urlData variable is concatenated to the end of the URL and used to navigate the page to the GetInfo.asp page where we extract the data.
Turning to the GetInfo.asp page, we start with the following code:
<%@ language = JavaScript%>
This tells the server that we are using the JavaScript language for our scripting. Then, within the body of the page we use the following:
Name : <%= Request.QueryString("Name") %>
This retrieves the Name value from the URL and writes it to the page. The following code does the same for the Age data.
Age : <%= Request.QueryString("Age") %>
We saw in Chapter 11 that we can create something called a cookie. A cookie can hold small amounts of information and is stored on the user's computer. Normally any variables stored in a web page disappear when we leave the web page. However, using cookies we can store information and specify when it should expire, that is, when it should cease to exist. For example, we can set a cookie to last for six months, and until it expires, we can read and write its information, even if a different page has been navigated to or the browser has been closed by the user.
We also saw that cookies are particular to a website—that is, to a particular domain. Cookies on a page in www.somedomain.com cannot be read by a page in www.someotherdomain.com. In fact, all the cookies for a domain are passed to the server when we request a page in that domain. For example, if we write client-side code that sets a cookie called UserName with the value "Paul" in a page in www.mydomain.com, when we next request a page from the same website, the cookie name and its value will be sent along with the rest of the request. This means that we are actually able to read cookies server-side that have been set on the client-side. The important thing to remember is that although we can read the cookies server-side, their data is actually always physically stored on the client computer. There is no cookies file on the server.
We will see later how we can create and modify cookies server-side, but again, this is done by the server sending the cookie and its information as part of the response. The browser picks up the cookie information from the response, and then creates the cookie on the client machine.
The information regarding cookies in Chapter 11 (for example, about expiration dates, path names, and so on) applies also to cookies accessed on the server.
To read a cookie, we use the Request object's Cookies collection property. As with the Form collection, we use the name given to a cookie to read its value.
For example, to retrieve a cookie called BakedFreshToday we just write the following:
var myCookieValue = Request.Cookies("BakedFreshToday");
We can use this technique to read cookies that have been set either server-side or client-side.
The request sent to a server usually contains extra information about the client computer and browser. When the request arrives at a server, the server takes this information and stores it in various server variables. These variables are read-only, meaning that we can read their values, but we can't set their values. Setting values can be done only by the server when it receives a request. The variables give us information such as the user's browser and operating system, the name of the server, the actual physical path of the file, and much more. Note that as far as browser information goes it can be a little misleading. Some browsers spoof other browsers; for example, Opera often "pretends" to be a version of Internet Explorer. So any information about the browser obtained server-side has to be taken with a pinch of salt.
The Request object allows direct access to the server variables. Again, like the Form and Cookie collection properties, there is a ServerVariables collection property of the Request object. For example, to get information contained in a server variable called NameOfServerVariable we just type the following:
Request.ServerVariables("NameOfServerVariable")
Of course, there is not really a server variable called NameOfServerVariable. The server variables have fixed names because they are set by the server. Here, we'll just look at one of the server variables, HTTP_USER-AGENT. The value returned by the code is the same as the client-side navigator object's userAgent property, which we saw in Chapter 5:
Request.ServerVariables("HTTP_USER-AGENT")
We've previously looked at the userAgent property of the navigator object, using it to create some JavaScript functions that detected the browser name, version, and operating system. In this example, we'll create server-side versions of those functions.
<%@ language = JavaScript%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <body> <%= Request.ServerVariables("HTTP_USER-AGENT") %> <% function getBrowserName() { var lsBrowser = new String(Request.ServerVariables("HTTP_USER-AGENT")); if (lsBrowser.indexOf("MSIE") >= 0) { lsBrowser = "MSIE"; } else if (lsBrowser.indexOf("Gecko") >= 0) { lsBrowser = "GECKO"; } else if (lsBrowser.indexOf("Mozilla") >= 0) { lsBrowser = "NETSCAPE"; } else { lsBrowser = "UNKNOWN"; } return lsBrowser; } function getOS() { var userPlat = "unknown"; var navInfo = new String(Request.ServerVariables("HTTP_USER-AGENT")); if ((navInfo.indexOf("Windows NT") != -1) || (navInfo.indexOf("Windows 95") != -1 ) || (navInfo.indexOf("Windows 98") != -1 ) || (navInfo.indexOf("WinNT") != -1 ) || (navInfo.indexOf("Win95") != -1 ) || (navInfo.indexOf("Win98") != -1 )) { userPlat = "Win32"; } else if(navInfo.indexOf("Win16") != -1) { userPlat = "Win16"; } else if(navInfo.indexOf("Macintosh") != -1) { userPlat = "PPC"; } else if(navInfo.indexOf("68K") != -1) { userPlat = "68K"; } return userPlat; } function getBrowserVersion() { var findIndex; var browserVersion; var browser = getBrowserName(); browserVersion = new String(Request.ServerVariables("HTTP_USER-AGENT")); if (browser == "MSIE") { findIndex = browserVersion.indexOf("MSIE") + 5; browserVersion = parseFloat(browserVersion.substring(findIndex, findIndex + 4)); } else if (browser == "GECKO") { findIndex = browserVersion.indexOf("Netscape") + 9; browserVersion = parseFloat(browserVersion.substring(findIndex, findIndex + 4)); } else if (browser == "NETSCAPE") { findIndex = browserVersion.indexOf("Mozilla") + 8; browserVersion = parseFloat(browserVersion.substring(findIndex, findIndex + 4)); } else if (browser == "UNKNOWN") { browserversion = 0; } return browserVersion; } %> <P> Browser is <%=getBrowserName()%> <br> Operating system is <%=getOS()%> <br> Browser Version is <%=getBrowserVersion()%> </P> </body> </html>
Save the file as ServerBrowserDetect.asp in the physical directory of the virtual directory AWalkOnTheServerSide. Load it into various browsers to see what sort of results you get.
Here are some of the results we're likely to see.
First is Internet Explorer 6 running under Windows XP, as shown in Figure 16-34.
Next, we have Netscape 7.1 running under Windows 2000, as shown in Figure 16-35.
The detection routines in the code give us the ability to check whether the browser is Netscape or IE, whether the browser is running on Windows32, and what the browser version is. If we require more sophisticated checking, we can modify the code or we can use the browser capabilities component that is available with IIS (which we'll look at later). Note that this is not a 100-percent foolproof method; many of the minor browsers "pretend" to be one of the main browsers, such as IE or Netscape.
This code is essentially very similar to the client-side version. At the top of the page, the following code writes out the contents of the HTTP_USER_AGENT server variable to the page.
%= Request.ServerVariables("HTTP_USER-AGENT") %>
Then, near the bottom of the page, the following code writes the return values of three functions, getBrowserName(), getOS(), and getBrowserVersion(), to the page.
Browser is <%=getBrowserName()%> <br> Operating system is <%=getOS()%> <br> Browser Version is <%=getBrowserVersion()%>
We'll look at each of these functions in turn.
For detecting the browser name we have the getBrowserName() function. First, we obtain the value of the server variable HTTP_USER-AGENT. The reference Request.ServerVariables("HTTP_USER-AGENT") actually returns an object, an item in the ServerVariables collection, so we need to create a new String object based on its value. That way we can use String methods, such as indexOf().
function getBrowserName() { var lsBrowser = new String(Request.ServerVariables("HTTP_USER-AGENT"));
Then in the if statement, we use indexOf() to check whether MSIE is contained in the lsBrowser string. If it is, we know that the browser is Internet Explorer and set the variable lsBrowser to MSIE.
if (lsBrowser.indexOf("MSIE") >= 0) { lsBrowser = "MSIE"; }
We then check whether it's a Navigator 6 or later browser.
else if (lsBrowser.indexOf("Gecko") >= 0) { lsBrowser = "GECKO"; }
Otherwise, we check in an else if statement for Mozilla. If that's present, we make the assumption that the browser is Netscape or at least a Netscape-compatible browser, and set the variable lsBrowser to NETSCAPE. This is sufficient most of the time, but there may be some unusual browsers that fail to work with this.
else if (lsBrowser.indexOf("Mozilla") >= 0) { lsBrowser = "NETSCAPE"; }
Finally, if Mozilla is not found, we set the variable lsBrowser to UNKNOWN in the final else statement.
else { lsBrowser = "UNKNOWN"; }
The variable lsBrowser is then returned by the function:
return lsBrowser; }
The operating system detection function, getOS(), is virtually identical to the corresponding client-side version. In fact, the only difference is that the variable navInfo is not the navigator object's userAgent property, but the HTTP_USER-AGENT server variable, which happens to be the same thing.
function getOS() { var userPlat = "unknown"; var navInfo = new String(Request.ServerVariables("HTTP_USER-AGENT"));
We then simply search the value of navInfo for different variations of operating systems. A series of if and else if statements use the String object's indexOf() method to search for the various names of operating systems. If the name is found, we set the userPlat variable to store a generic name for those operating systems. For example, for Windows 95, 98, NT, and 2000, we set the userPlat variable to Win32.
The first if statement is the most complex because its condition is a series of indexOf() methods. If any of these don't return -1, the condition is treated as true by the if statement. Remember that when using an OR operator only one of the conditions needs to be true for the whole condition to be true.
if ((navInfo.indexOf("Windows NT") != -1) || (navInfo.indexOf("Windows 95") != -1 ) || (navInfo.indexOf("Windows 98") != -1 ) || (navInfo.indexOf("WinNT") != -1 ) || (navInfo.indexOf("Win95") != -1 ) || (navInfo.indexOf("Win98") != -1 )) { userPlat = "Win32"; }
The other conditions checking for other operating systems are simpler.
else if(navInfo.indexOf("Win16") != -1) { userPlat = "Win16"; } else if(navInfo.indexOf("Macintosh") != -1) { userPlat = "PPC"; } else if(navInfo.indexOf("68K") != -1) { userPlat = "68K"; }
Finally, the variable userPlat is returned by the function:
return userPlat; }
We'll finally look at the function that returns the browser version: getBrowserVersion(). This function is again similar to the client-side version. We first find out which browser we're dealing with, by setting the variable browser to the return value of the getBrowserName() function. Then we store Request.ServerVariables("HTTP_USER-AGENT") as a new String object in the browserVersion variable.
function getBrowserVersion() { var findIndex; var browserVersion; var browser = getBrowserName(); browserVersion = new String(Request.ServerVariables("HTTP_USER-AGENT"));
With Internet Explorer, the version number is always right after the characters MSIE in the HTTP_USER__AGENT string. So, in the if statement, we check whether the variable browser has value MSIE and if it does, we search for MSIE in browserVersion, add 5 onto its index in the string, and store this in the variable findIndex. To get the version number, we cut out the substring from an index of findIndex, to an index of findIndex + 4, from the string browserVersion, and then resave this as browser_Version.
if (browser == "MSIE") { findIndex = browserVersion.indexOf("MSIE") + 5; browserVersion = parseFloat(browserVersion.substring(findIndex, findIndex + 4)); }
With Netscape 6 and later the version number appears after the word Netscape.
else if (browser == "GECKO") { findIndex = browserVersion.indexOf("Netscape") + 9; browserVersion = parseFloat(browserVersion.substring(findIndex, findIndex + 4)); }
With Netscape 4 and earlier, the version number appears right after the characters Mozilla/ in the HTTP_USER_AGENT string. So, in the else if statement, we use the same method as for the Internet Explorer version to get the browser version stored in the variable browserVersion.
else if (browser == "NETSCAPE") { findIndex = browserVersion.indexOf("Mozilla") + 8; browserVersion = parseFloat(browserVersion.substring(findIndex, findIndex + 4)); }
In the final else if statement, we check whether the variable browser has the value UNKNOWN, in which case we set browserVersion to 0.
else if (browser == "UNKNOWN") { browserversion = 0; }
The variable browserVersion is then returned by the function:
return browserVersion; }
This method of browser detection works fine in most cases, but as we'll see later it's possible to do more sophisticated browser checking.
We'll now move on from the Request object to the Response object. This object deals with the out-going side of things on the server. It allows us to write information to the page sent to the user's browser, to create cookies, and to redirect the user's browser to a page different from the one requested. We'll now look at how each of these is achieved.
We've already been writing content to the page using the <%= and %> notation.
However, this is actually a shorthand for Response.Write(), where Write() is a method of the Response object. For example, to write "Hello World" to the page using Response.Write(), we type the following:
<% Response.Write("Hello World"); %>
While the <%= %> notation is often convenient, there are times where it's not suitable for use. For example, it is not suitable when the text we want to write to the page is built up with a number of lines of JavaScript in a block, perhaps including a number of loops and if statements.
Let's look at an example. Imagine that we want to calculate the cost of shipping an item, which is dependent on the country the item is being shipped to.
First let's see how this could be done with Response.Write().
<%@ Language="JScript" %> <% var shippingCost = 0; var custCountry = Request.Form("txtCountry"); if (custCountry != "Rest Of World") { if (custCountry == "USA") { shippingCost = 4.95; } else if (custCountry == "Canada") { shippingCost = 5.95; } Response.Write("<P>Shipping will cost $" + shippingCost + "</P>"); } else { Response.Write("Sorry we don't currently ship to that country"); } %>
Now let's see it done using the <%= %> notation.
<%@ Language="JScript" %> <% var shippingCost = 0; var custCountry = Request.Form("txtCountry"); if (custCountry != "Rest Of World") { if (custCountry == "USA") { shippingCost = 4.95; } else if (custCountry == "Canada") { shippingCost = 5.95; } %> <P>Shipping to your country will cost $<%= shippingCost %></P> <% } else { %> <P>Sorry we don't currently ship to that country</P> <% } %>
Two main problems exist with the second approach of using the <%= %> notation.
The first is that the Response.Write() method allowed us to do all our server-side scripting in just one server-side script block; that is, all the code was contained in one set of <% and %>. This makes it easier for the server to find and process our code, and therefore reduces the load on the server and speeds up our page. In comparison, the second method, with <%= and %>, needs four server-side script blocks, making life harder on the server because it has to search out each individual block. When we're creating client-side code containing many script blocks, this is not a problem because even the most modest processor can cope with this load. However, a server machine might have to cope with thousands, or maybe tens or hundreds of thousands, of users requesting a page at the same time, that is, concurrently. In this case, a very small amount of extra processing is multiplied many times and could cause loading problems.
The second disadvantage of the <%= %> notation is that, in this case, it has made the code's flow less clean and easy to read. This may not be a problem in a short piece of code like this, but can make life tricky if it's a big page with many separate script blocks.
We saw earlier how the Request object allows us to read cookies stored on the client machine. Let's now look at how we can create them with the Response object.
Just as with client-side cookie creation, we can use the Response object to specify the cookie's name, its value, and also, optionally, the date it should expire, the domain on which it's valid, the path on which it's valid, and whether it should be sent only via secure means over an HTTPS connection. We do this using the Cookies collection property of the Response object.
For example, to create a cookie named UserName with the value Paul that expires on Jan 1, 2010, we type the following:
Response.Cookies("UserName") = "Paul"; Response.Cookies("UserName").Expires = "Jan 1, 2010";
As we saw before, to read this back we simply use the Cookies collection property of the Request object. We can now use the Response.Write() method to write the value of the cookie to the page.
var myUserName = Request.Cookies("UserName"); Response.Write(myUserName);
As you can see, creating and reading cookies is actually a lot easier server-side than it is client-side.
The ability to redirect the user to a page other than the requested one is very useful. For example, we could redirect users to a page that is compatible with their browser. Also, if we have incorporated some sort of security into our site, such as a logon system for access to part of our website, we can quickly redirect invalid users.
To redirect the user, we simply use the Response object's Redirect() method, and we pass it the URL of the page we want to redirect to as a parameter. For example, to redirect a user to http://locahost/AWalkOnTheServerSide/IllegalUser.htm we write the following:
There's another way of redirecting the user if we are using ASP 3, which is installed on Windows XP or Windows 2000, and that's the server object's Transfer() method (we'll look at the server object later in the chapter but now seems like a good time to mention the Transfer() method). With Response.Redirect() any form data passed to the page is lost when the user is redirected to the new page. Basically, Response.Redirect() sends a message to the user's browser telling it to request a new page, the one specified in the method's parameter. The browser makes the request and the server responds.
Server.Transfer is more efficient because the redirection is all done on the server. The browser is unaware that a page other than the one actually requested is coming back. Also, all the form data passed and all the ASP objects and their information is retained and passed along to the new page. Use of the method is as follows:
Server.Transfer(new_page_url)
So we write our previous example like this:
Server.Transfer("/AWalkOnTheServerSide/IllegalUser.asp");
Let's use some of the knowledge we've gathered so far on cookies and redirection to create a simple logon system that restricts certain parts of a website to valid users. It's not the sort of security that is suitable for keeping credit card details safe on an e-commerce website, but it would be sufficient for slightly sensitive pages on an intranet.
We'll create three pages. The first allows the user to log on, the second tests whether the user is a valid user, and the third is the private page that should only be reached by a valid user.
First, let's create the page that will let the user log on to the system.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <script language=JavaScript> function form1_onsubmit() { var form = document.form1; var returnValue = false; if (form.txtUsername.value == "") { alert("Please enter your username"); form.txtUsername.focus(); } else if (form.txtPassword.value == "") { alert("Please enter your password"); form.txtPassword.focus(); } else { returnValue = true; } return returnValue; } </script> </head> <body> <P> To access this website please enter your username and password in the boxes below </P> <form action="CheckLogOn.asp" method=post id=form1 name=form1 onsubmit="return form1_onsubmit()"> <P>Username : <input id=txtUsername name=txtUsername type=text></P> <P>Password : <input id=txtPassword name=txtPassword type=password></P> <P> <input id=reset1 name=reset1 type=reset value=Reset> <input id=submit1 name=submit1 type=submit value="Log On"> </P> </form> </body> </html>
Save this as Logon.htm in the physical directory of the AWalkOnTheServerSide virtual directory on your web server.
Next, we have the page that actually checks the validity of the logon information.
<%@ language = JavaScript%> <% if (Request.Form("txtUsername") == "SecureUser" && Request.Form("txtPassword") == "letmethrough") { Response.Cookies("IsValid") = "Yes"; Response.Redirect("SecureHome.asp"); } else { Response.Redirect("Logon.htm") } %>
Save this page as CheckLogOn.asp in the same directory as Logon.htm.
Finally, let's create a simple, secure page that's only accessible if the user is valid.
<%@ language = JavaScript%> <% if (Request.Cookies("IsValid") != "Yes") { Response.Redirect("Logon.htm") } %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <body> <P align=center> <font size=5>This page is for valid users only</font> </P> </body> </html>
Save this as SecureHome.asp, again in the same directory as Logon.htm.
Now browse to the Logon.htm page, for example using the following URL:
http://localhost/AWalkOnTheServerSide/Logon.htm
We'll see the screen shown in Figure 16-36.
The valid username is SecureUser and the password is letmethrough. Enter these valid values and click the Log On button. We will be taken to the secure home page SecureHome.asp, displaying the text "This page is for valid users only." However, if we enter an invalid value and click the Log On button, we will find ourselves back at the logon page.
Note that the Reset button on the logon page is there for the user's convenience. It allows users to clear the username and password by clicking the button rather than having to delete each character in both boxes if they've entered the wrong username or password.
Close the browser; then try navigating to SecureHome.asp directly, avoiding the logon page.
http://localhost/AWalkOnTheServerSide/SecureHome.asp
The logon page simply consists of a text box and a password box, and a submit button and a reset button. When the submit button is clicked, the code connected to the form's onsubmit event handler in the <form> tag's definition, shown in the following code, is executed. In other words, the form1__onsubmit() function, which we defined in the head of the page, is executed. This performs some validity checking on the form data.
<form action="CheckLogOn.asp" method=post id=form1 name=form1 onsubmit="return form1_onsubmit()">
The function form1_onsubmit() simply checks that the user has entered values for both the username and password. The function uses two if statements to check that the value properties of the objects associated with the txtUserName and txtPassword input boxes are not empty strings. If they are, we alert the user and stop the form from being posted by returning false. Remember that if we return true to the onsubmit event handler, the form submission continues; if we return false it is canceled and no post to a new page occurs.
But why do this? Surely, we could let the form be submitted to the server where the empty input boxes will be spotted as invalid and the user will be returned to this page.
Well, to start with, our aim should usually be to limit the amount of processing the server has to do. On websites with few visitors, it won't be such an issue, but if one day we find ourselves writing for servers with tens of thousands of concurrent users, we'll be glad we got into the practice of easing pressure on the server. The amount of processing required to check for empty values is very small and certainly no great burden on the client computer. However, if it's done on a high load server, this burden might be multiplied by tens of thousands or more, and our server would struggle.
A second advantage of doing this validation client-side is that we can tell the users there and then that there is a problem with the data they are submitting, rather than letting them submit the form and then telling them.
However, if this were a live website and the access was to vital information, we'd be wise to have similar server-side checks in addition to the client-side checks just to stop malicious hackers from trying to post to the CheckLogon.asp page from their own version of Logon.htm, circumventing our client-side checks. However, for this example what we have is fine.
Let's turn our attention to CheckLogon.asp. This is the page that the form data is posted to and that checks whether the username and password are valid.
As always, at the top of the page we tell the server that we'll be using JavaScript for all our scripting on this page.
<%@ language = JavaScript%>
Then we validate the values the user entered on the Logon.htm form. Here we simply see if the username is SecureUser and the password is letmethrough. In reality, we'd use a system that allows many usernames and passwords to be checked, such as a database or a directory service that also contains user validation details.
In the first if statement we check whether the username and password are valid. If they are, we set the cookie IsValid to Yes. We can use this cookie on each secure page to check whether the user has logged on and is valid, just in case the user tried to access a page directly without logging on. We haven't set an expiration date for the cookie, so it will expire as soon as the user closes the browser. Finally, we redirect the user to the SecureHome.asp page, so in effect the users never actually see the CheckLogon.asp page; it's just a page they pass through.
<% if (Request.Form("txtUsername") == "SecureUser" && Request.Form("txtPassword") == "letmethrough") { Response.Cookies("IsValid") = "Yes"; Response.Redirect("SecureHome.asp"); }
In the else statement, which is executed if the user details are not valid, we redirect the user back to the Logon.htm page to enter the correct username and password.
else { Response.Redirect("Logon.htm"); } %>
Finally, let's look at the SecureHome.asp page, which our users are redirected to from CheckLogon.asp if they entered the correct user details.
Again, the page starts by setting the language for the script.
<%@ language = JavaScript%>
The page then checks to see whether the cookie IsValid has the value Yes. If not, the user is redirected away from this page and to the Logon.htm page.
<% if (Request.Cookies("IsValid") != "Yes") { Response.Redirect("Logon.htm"); } %>
Finally, we have the body of the page containing the secure information. Obviously, in a real application this would contain information for which there was a need for user validation.
<html> <body> <P align=center> <font size=5>This page is for valid users only</font> </P> </body> </html>
The Server object, as its name suggests, represents the server itself. We're going to look at how it can be used to create components that run on the server; but what do we mean by components?
Think of components as a little like plug-ins for a server. They are small programs written in languages, such as C++ and Visual Basic, that we can create as objects in our server-side code and use to perform functions beyond those a basic web server can perform. For example, a credit card component might validate credit card numbers, and an e-mail component may allow web pages to send and receive e-mails. IIS comes with a number of components already installed, such as a file access component that allows us to create new files and directories on the server, a page hit counter component, and a component that checks the capabilities of browsers, which we'll be looking at shortly.
To create an object based on a component installed on the server, we need the Server object's Create_Object() method. To tell the server which component we want to create an object for, we need to pass this method the programmatic ID (ProgID) for the component, something that we'll need to find from the component's documentation.
A single component may actually be able to create a number of different objects, each with a different function and each represented by a different ProgID.
As said previously, Personal Web Server includes the browser capability component. Let's now look at how we can make use of it.
The browser capability component uses information sent in the HTTP request header to work out not only information such as the browser name and version number, but also details such as whether the browser supports JavaScript, cookies, HTML frames, and much more. However, it won't tell you if the user has turned off or disabled some of these features. How does the browser capability component work out all this information?
The key to the component's knowledge is a text information file, which contains details about various browsers. The file is called browscap.ini and is usually found in the windows\system32\inetserv folder. Of course, new browsers come out all the time so it's important to update this file, either by altering it ourselves or, even better, by downloading a new one. More information is available from http://www.cyscape.com/browscap/.
To get an idea of what sort of properties the browser capability component supports, take a look at the browscap.ini file on your own system, which divides into sections for various browsers with each browsers' properties listed. The following is just a very small bit of the browscap.ini file on my computer.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; IE 5.0 [IE 5.0] browser=IE Version=5.0 majorver=5 minorver=0 frames=True tables=True cookies=True backgroundsounds=True vbscript=True javaapplets=True javascript=True ActiveXControls=True Win16=False beta=True AK=False SK=False AOL=False Update=False [Mozilla/4.0 (compatible; MSIE 5.*; Windows 95*)] parent=IE 5.0 platform=Win95 beta=True [Mozilla/4.0 (compatible; MSIE 5.*; Windows 98*)] parent=IE 5.0 platform=Win98 beta=True [Mozilla/4.0 (compatible; MSIE 5.*; Windows NT*)] parent=IE 5.0 platform=WinNT beta=True [Mozilla/4.0 (compatible; MSIE 5.*; Windows 2000*)] parent=IE 5.0 platform=Win2000 beta=True [Mozilla/4.0 (compatible; MSIE 5.*)] parent=IE 5.0
In this example, we'll create a page that uses the browser capability component to list some of the more useful properties of the browser that the user is using.
<%@ language = JavaScript%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <body> <P>Your Browser's details</P> <% var BrowsCapComponent = Server.CreateObject("MSWC.BrowserType"); %> Browser Name : <%= BrowsCapComponent.browser %> <br> Browser Version : <%= BrowsCapComponent.version %> <br> Supports JavaScript : <%= BrowsCapComponent.javascript %> <br> Supports Frames : <%= BrowsCapComponent.frames %> <br> Supports Cookies : <%= BrowsCapComponent.cookies %> <% BrowsCapComponent = null; %> </body> </html>
Save this file as BrowserCapability.asp in the physical directory of the AWalkOnTheServerSide virtual directory of your server. Then browse to it using the following URL:
http://localhost/AWalkOnTheServerSide/BrowserCapability.asp
When we load the page into our browser we should see information like that shown in Figure 16-37. This was from Internet Explorer 6.
If, on the other hand, we get information like that shown in Figure 16-38, we need to update our brows_cap.ini file because our browser and/or operating system were released after the browscap.ini file was created. The browscap.ini file can tell us only about browsers and operating systems that exist at the time the file was created. You may find that even an up-to-date browscap.ini seems to identify particular browsers incorrectly, quite often even common browsers. In this case, we need to alter the browscap.ini file, use a different browser detection component, or use the browser detection scripts we created previously.
We start the page as usual by defining the language to be used in the script.
<%@ language = JavaScript%>
We then create an object for the browser capability component on the server using the Server object's CreateObject() method.
var BrowsCapComponent = Server.CreateObject("MSWC.BrowserType");
MSWC.BrowserType is the browser capability component's ProgID.
Now, with the component's object created, we can access its properties as we would any object. In the lines that follow, we find out the browser's name, version, and whether the browser supports JavaScript, frames, or cookies. Here we just write the results to the page, but in practice, we could make use of this information to redirect a browser to a page with content it supports or by only writing content to the current page if the browser supports it.
Browser Name : <%= BrowsCapComponent.browser %> <br> Browser Version : <%= BrowsCapComponent.version %> <br> Supports JavaScript : <%= BrowsCapComponent.javascript %> <br> Supports Frames : <%= BrowsCapComponent.frames %> <br> Supports Cookies : <%= BrowsCapComponent.cookies %>
On the final line, we set the variable BrowsCapComponent, which holds the reference to the created component, to null.
BrowsCapComponent = null;
But why do this? Well, as long as the reference to the component held by a variable remains in scope, that is, available for scripting, it will remain in memory. We want to limit the load on the server and in particular, memory usage, so by setting the variable to null we remove the reference, and the server can unload the component and free up spare memory on the server.
The first question is, what is a server-side include? One of the main uses of a server-side include (SSI) is to include one file inside another. Before the server starts any processing of our web page's server-side code, it does a preprocessing check for server-side include directives. This is simply an instruction made by us to insert the contents of an .asp page, .htm page, or any valid text file inside the current page. We do this with the #include directive.
OK, let's say we have two files, MyWebPage.asp and MyOtherPage.inc. MyWebPage.asp looks like this:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <body> <H2>My Main Page</H2> <!-- #include file="MyOtherPage.inc" --> </body> </html>
We've used #include to insert the file MyOtherPage.inc and the contents of that file will replace the #include line. The file="" must be set to a file name and file path. If this file is in the same directory as the MyWebPage.asp file, then there's no need to use the path. If it's not, we must include the whole path.
<!-- #include file="C:\MyIncludeFiles\MyOtherPage.inc" -->
Now, if our MyOtherPage.inc file looks like this:
<H2>My Include File</H2> <P>This is included in another page</P> <P>Included with the include directive</P>
then the #include file directive will make the server include all of this file at the point of the #include and send this page to the client.
<html> <body> <H2>My Main Page</H2> <H2>My Include File</H2> <P>This is included in another page</P> <P>Included with the include directive</P> </body> </html>
Although this is the page that's sent to the user's browser, it's all done in the server's memory. No changes are made to either MyWebPage.asp or MyOtherPage.inc. The use of the suffix .inc in the included file's name is not mandatory, but it helps us to keep track of what the file is used for.
We can include server-side or client-side script in either the included page and/or the page it's included inside. The only proviso is that the page that is sent to the user's browser, with all of its server-side includes, must be a valid page in terms of HTML and script. For example, if MyOtherPage.inc is as follows, the code would not be valid because the page that is being inserted into it already has a </body> and </html> tag and only one set of these is allowed per page.
<H2>My Heading</H2> </body> </html>
Also, we can't include inside a script block; for example, the following is invalid:
<% <!-- #include "script.js" --> // my code %>
Instead we would ensure the code in the script.js file is inside script tags.
<% // my included code %>
Then include this in the main file:
<!-- #include "script.js" --> <% // my code %>
Server-side includes are useful when we have the same bit of code or same bit of HTML used in more than one page on our website. Take for example our client-side cookie functions; we may well use those in many pages on a website. We could use frames and put the cookie code in there, but what if we don't want frames? Well, we can just include the code wherever we want it with server-side includes. It also reduces the size of our pages on the server if we are using the same script functions or HTML repeatedly, because we have to store this information only once.
Using server-side includes also means that we can make our pages more modularized, by separating script from content. This means that if we want to update our code, we can update just the include file and all of our pages will automatically be changed to reflect our updates.
Just remember that any included script must be contained in the correct script tags. For example, if MyOtherPage.inc looked like the following, this it would be fine.
<% // My server-side JavaScript %>
It could look like this for client-side JavaScript:
<script> // My client-side JavaScript </script>
But just including some script would not be fine. We just need to imagine what the final page will look like after all the #includes have been processed. Will it be valid HTML or valid script? If it won't, the page will fail.
The next ASP object we'll be looking at is the Session object. This object enables us to keep track of users and to store information temporarily on the server, which we can use from any page during the user's visit to our website.
For example, if a section of our website requires a user to log on before viewing the pages, we would force the user to log on. However, having logged on once during a visit to our website, we don't want users to have to enter a usernames and passwords again and again every time they go to a different secure page. The solution is that we log them on once and store this information using the Session object. We can then use this logon information when they go to a different page.
A Session object embodies the idea of a user session, which is a user's visit to our website. A user session starts when a user first browses to an ASP page on our website and ends twenty minutes after the user last requests a page, although it is possible to set this time lapse differently. The only problem with the time delay is that it could be that the user didn't actually leave the website, but simply spent longer than twenty minutes looking at a single page. In this case, as soon as the user moves to another page a new session will be created.
Also, note that sessions are specific to a particular virtual directory, so if the user starts by loading a page in AWalkOnTheServerSide, and then loads a page in AnotherVirtualDirectory, this will start a new session.
Every session is automatically assigned a session ID that is unique to that user for that session; it will be different if that user returns later and creates a new session. We can retrieve this session ID using the Session object's SessionID property.
var thisSessionID = Session.SessionID
The server actually uses cookies to keep track of SessionID values, so if the user's browser doesn't support cookies or they are turned off, the SessionID can't be stored, nor can the session itself be properly created.
The Session object is one of the few ASP objects that actually has events as well as properties and methods. There are two events: Session_OnStart and Session_OnEnd. These events have names that differ in syntax from the BOM events we have looked at so far, and as we will see shortly, they are dealt with differently in code.
The names of the events give away when they fire; they fire automatically when the user first requests an ASP page and when twenty minutes has elapsed since the user last requested a page. In other words, they fire at the start and end of a user's session. We can see the order of events in the diagram in Figure 16-39.
Because the Session_OnStart and Session_OnEnd events could fire on any page in our web application, we need to connect them to code in a special file not linked with any particular page. This file is named global.asa; we must use exactly this name and can't make up our own name.
To connect code to these events, we write the following:
function Session_OnStart() { // Code that executes when the session starts } function Session_OnEnd() { // Code that executes when the session ends }
When we previously used events, we needed to connect an event handler to the function that we wanted to execute on the occurrence of a specific event. However, in the case of these events, that is not necessary. The server automatically knows when these functions should be executed.
It's important to note that the Session_OnStart and Session_OnEnd events fire only when the first ASP page is requested; they don't fire if it's a static HTML page; that is, any page saved with the .htm or .html extension. Also important to note is that browsers that don't support cookies or those with cookies disabled won't properly support sessions. In this case, every time the user requests an ASP page, the Session_OnStart event will fire.
There can be only one global.asa file for each root directory or root of a virtual directory, so we'll find one in wwwRoot and we can put one in each physical directory corresponding to a virtual directory we define. The contents of the global.asa file are accessed automatically by the web server when a new session starts or times out.
We can't write information to a page from the global.asa, but we can initialize session variables to be used later in our pages, so let's look at creating and using session variables.
The Session object allows us to store information on the server for the life of the session in session variables. We can use this information throughout the session regardless of which page the user is on. For example, imagine we requested that the user log on with a username and password. If we store this information in session variables, then later on if we need that information, say for looking up more information in a database, we don't need to ask the user for the details again.
To create a session variable we simply give it a name and value.
Session("MySessionVariableName") = "MyVariableValue"
The preceding line will store the value MyVariableValue in the session variable MySessionVariable_Name. We can store numbers, strings, and even objects, such as a Date object, in session variables.
To get the information out again, we just write the following line:
var MySessionVariableValue = Session("MySessionVariableName")
This code will store the value of the MySessionVariableName session variable in the variable MySessionVariableValue.
If we wish, we can initialize session variables in the Session_OnStart event code, within the global.asa page.
function Session_OnStart() { Session("MySessionVariableName") = "InitialValue" }
We need to stress that session variables are not cookies and, unlike cookies, they are stored in the memory of the server and not on the client computer. In addition, we can't read or write them directly from client-side script, although of course, form information passed from the client-side to the server could then be stored in a session variable.
However, because sessions require cookies, we'll find that session variables are also limited to cookie-enabled browsers. If we want to make use of sessions, we need to inform the user that cookies are required or direct the user to a page not requiring sessions and cookies. Alternatives to using session variables are to pass the information in a URL or create a form with hidden input boxes and post the information to the next page in this way.
We also need to avoid going crazy with the amount of data we store in session variables since all this information takes up precious memory on the server. If we have large numbers of sessions going at one time and we're using up a lot of memory for each session, we'll find, at best, the server goes very slowly, and at worst, it's brought to a virtual standstill. So, we must be judicious with our use of session variables. Always ask whether you really need to keep track of this information, or whether there is a more convenient way of keeping track of the information without overloading the server.
The last of the ASP objects that we will look at is the Application object. This represents our website as a whole. It might perhaps have made more sense to call it the "Web site" object as, generally speaking, that's what it is.
All that we have seen so far on passing information from a client-side form to an ASP page on the server, of setting and reading cookies, and of passing information in URLs, has been either page-specific or specific to the individual user browsing the website. Even sessions and session variables are specific to one user. There is no way that we can share information between users. We have variables with global page scope, but no variables with global website scope, or at least we don't unless we use the Application object.
The main purpose of the Application object is to allow us to define information that we can access server-side on any page for any user. Just as we can define session variables by writing the name and assigning a value, so too we can use the Application object to store variables called application variables whose scope is the entire website, or at least the virtual directory.
For example, to set an application variable called MyAppVariable and with the value "ABC", we'd write the following:
Application("MyAppVariable") = "ABC";
To read it back, we just write this:
var myVar = Application("MyAppVariable");
Just as the Session object has the Session_OnStart and Session_OnEnd events, the Application object also has its start and stop events, Application_OnStart and Application_OnEnd. Again, these are created in the global.asa file.
Application_OnStart fires when the web server is first started and the first web page is requested. For example, it fires after the operating system has been rebooted, or if you manually stopped and then restarted the web server using the server console. As we can imagine, the Application_OnEnd event occurs when the web server is stopped, again because either the operating system is being rebooted or we have manually stopped the web server.
For example, if we want to initialize the application variable MyAppVariable to "XYZ" as soon as the server starts, we'd add this to the global.asa file of the virtual directory we want affected:
function Application_OnStart() { Application("MyAppVariable") = "XYZ"; }