Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 50 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
50
Dung lượng
0,91 MB
Nội dung
Loading Pages with the XMLHTTP Object The process for using the XMLHTTP object is relatively simple, especially if you are happy to load the new page synchronously. You can create an instance of the XMLHTTP object by using the following: var oHTTP = new ActiveXObject(“Microsoft.XMLHTTP”); Next you open an HTTP connection, specifying the HTTP method (usually “GET” or “POST” ), the URL of the target resource, and the value false to indicate that you want synchronous opera- tion. Then you can use the send method to send the request: oHTTP.open(“method”, target-url, false); oHTTP.send(); After the response has been received from the server, you test the status property (the value of the HTTP status header) to see if it is 200 (which means “OK” ) and extract the page as a string from the XMLHTTP object by using the following: if (oHTTP.status == 200) sResult = oHTTP.responseText; else // an error occurred However, if you use synchronous loading, the browser will not respond to any other events (including animating the GIF file) while the request for the next page is executing. Instead, you need to use asynchronous loading to allow the browser to carry on reacting as normal while the server creates and returns the new page. Asynchronous Loading with the XMLHTTP Object For asynchronous loading, you first have to specify the name of a callback function that will be executed each time the readystate property of the XMLHTTP object changes and specify true for the third parameter of the open method: oHTTP.onreadystatechange = myCallbackHandler; oHTTP.open(“method”, target-url, true); oHTTP.send(); The callback function you specify will be executed several times as the XMLHTTP object fetches the response from the server. When the response is complete, the value of the readystate property will be 4 , and at that point you can test for an error and extract the page as a string: function myCallbackHandler () { if (oHTTP.readyState == 4) { if (oHTTP.status == 200) sResult = oHTTP.responseText; else // an error occurred } } 3 Loading Progress and Status Displays 88 05 0672326744 CH03 5/4/04 12:25 PM Page 88 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 89 Displaying a Progress Bar Graphic Using the XMLHTTP Object in the Progress Bar Sample Page Listing 3.5 shows the client-side code included in the progress bar sample page. It works exactly as just demonstrated, with the only additions being a test to see that an instance of the XMLHTTP object was successfully created and the display of any error messages in a <span> element, located below the progress bar graphic in the page. LISTING 3.5 Loading the Results Page with XMLHTTP <script language=’javascript’> <!-- // variable to hold reference to XMLHTTP object var oHTTP; function loadTarget(sURL) { // create instance of a new XMLHTTP object oHTTP = new ActiveXObject(“Microsoft.XMLHTTP”); if (oHTTP != null) { // specify callback for loading completion oHTTP.onreadystatechange = gotTarget; // open HTTP connection and send async request oHTTP.open(‘GET’, sURL, true); oHTTP.send(); } else { document.all[‘spnError’].innerText = ‘ERROR: Cannot create XMLHTTP object to load next page’; } } function gotTarget() { // see if loading is complete if (oHTTP.readyState == 4) { // check if there was an error if (oHTTP.status == 200) { // dump next page content into this page document.write(oHTTP.responseText); } else { document.all[‘spnError’].innerText = ‘ERROR: Cannot load next page’; Information on the XMLHTTP Object You can find a full reference to the XMLHTTP object (effectively the XMLHTTPRequest inter- face) in the MSDN library, at http://msdn. microsoft.com/library/en-us/xmlsdk30/ htm/xmobjxmlhttprequest.asp . 05 0672326744 CH03 5/4/04 12:25 PM Page 89 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. } } } //--> One interesting point about this listing is in the gotTarget callback handler. After you’ve extracted the complete content of the new page as a string, you simply write it into the current browser window, using the client-side document.write method. This replaces the current content, giving the same output as in the first example in this chapter, after the main customer lookup process has completed (refer to Figure 3.5). What you’ve actually achieved here is to reload the same page again in the background, while still at Stage 2 of the process (displaying the “please wait” message and progress bar) and then use it to replace the current page. But because the URL you request contains the customer ID in the query string this time, the new page generated by the server will be the one for Stage 3 of the process (containing the DataGrid control, populated with the results of the database search). Altogether, this is a neat and interesting solution! The Changes to the HTML and Server Control Declarations in This Example The only remaining features of this example that we need to examine are how to initiate the client-side code that loads the results page and how to handle cases where client-side scripting is disabled in the browser. In the HTML section of the page, you declare the <body> element as a server control this time, by adding an ID and the runat=”server” attribute—just as you did for the <meta> element earlier in this chapter: <body id=”tagBody” runat=”server”> Then, in the Page_Load event handler, you can add an appropriate onload attribute to the opening <body> tag in the server-side code. Listing 3.6 shows the changed section of the Page_Load event handler. The only section that differs in this example from the first example is the part where the postback from Stage 1 occurs—where you are generating the “please wait” page for Stage 2 of the process. LISTING 3.6 The Page_Load Event Handler for the Progress Bar Example If Page.IsPostback Then Dim sRefreshURL As String = Request.Url.ToString() _ & “?custID=” & txtCustomer.Text ‘ if it’s IE, need to load new page using script because ‘ the META REFRESH prevents the animated GIF working If Request.Browser.Browser = “IE” Then tagBody.Attributes.Add(“onload”, “loadTarget(‘“ _ & sRefreshURL & “‘);”) 3 Loading Progress and Status Displays 90 LISTING 3.5 Continued 05 0672326744 CH03 5/4/04 12:25 PM Page 90 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 91 Displaying a Progress Bar Graphic ‘ set META REFRESH as well in case script is disabled ‘ use long delay so script can load page first if possible mtaRefresh.Attributes.Add(“http-equiv”, “refresh”) mtaRefresh.Attributes.Add(“content”, “30;url=” & sRefreshURL) Else ‘ not IE so use META REFRESH to start loading next page ‘ allow 3 seconds for progress bar image to load mtaRefresh.Attributes.Add(“http-equiv”, “refresh”) mtaRefresh.Attributes.Add(“content”, “3;url=” & sRefreshURL) End If frmMain.Visible = False divWait.Visible = True Else . You use the ASP.NET Request.Browser object, which exposes a property also named (rather confusingly) Browser . This property indicates the browser type, and if it is “IE” , you know that you are serving to an Internet Explorer browser—so we can add the onload attribute to the <body> element by using the Attributes collection of the HtmlGenericControl class that imple- ments it in ASP.NET. The result, when viewed in the browser, looks like this: <body id=”tagBody” onload=”loadTarget(‘/daveandal/books/6744 ➥/loadwait/progressbar.aspx?custID=a’);”> You also add a “catch all” feature in case scripting is disabled, by setting the attributes of the <meta> element. In this case, the <meta> element will cause a page reload after 30 seconds. You can also see in Listing 3.6 the changed value of the content attribute that you apply for non–Internet Explorer browsers, to allow the progress bar graphic to load before the redirection commences (as discussed earlier in this chapter). LISTING 3.6 Continued Checking for the Version of Internet Explorer In theory, you should test for the browser version as well as the type because the XMLHTTP object is available only in version 5 and higher of Internet Explorer. However, the “catch all” you build in for when scripting is disabled will also make the page work (after a fashion) on earlier versions of Internet Explorer. Whether anyone is still using version 4 or earlier, with all the security issues inher- ent in those versions, is open to discussion. 05 0672326744 CH03 5/4/04 12:25 PM Page 91 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. Implementing a Staged Page Load Process We hinted earlier in this chapter that there are ways you can generate “real” status messages in the browser while executing a complex or lengthy operation on the server. Although the tech- nique of simply flushing chunks of content back to the browser as the process runs does work, it’s not particularly efficient in terms of connection usage or server loading. Web servers are designed to receive a connection and resource request, generate the required response, and disconnect as quickly as possible to allow the next user to connect and make a resource request. Because it’s likely that most complex operations will involve database access on the server, holding open a connection to the database while you flush chunks of content back to the client is probably not a good idea. However, if you can break down the complex or lengthy process into separate individual stages, it is possible to provide useful “real” status feedback in the browser. In fact, it’s reasonably easy to do this in Internet Explorer 5 and higher, by using the XMLHTTP object used in the previous example. The Steps in Implementing a Staged Page Load Process Figure 3.7 shows a flowchart of a staged process that is implemented as the next example in this chapter. The main page, named stagedloading.aspx , uses the XMLHTTP component to request a separate operation page, named stagedfetchpage.aspx , four times. Each request contains, in the query string, a customer ID that the user provides and a step value that indicates which stage of the process is currently being performed. The operation page uses these values to collect the appropriate row set from the Northwind database at each stage and add to a DataSet instance a table that is stored in the user’s ASP.NET session. In between requests, the main page can display progress and status information, or it can display any error messages returned by the operation page. When the process is complete in this example, the value returned (the total for all matching orders) is displayed—together with a button that allows the user to view the list of orders. This data is in the DataSet instance stored in the user’s ASP.NET session, so it can be extracted and displayed without requiring another trip to the database. Of course, you can easily tailor this example to display different data at any stage and provide links to access any of the tables in the DataSet instance. In fact, this process opens up a whole realm of opportunities for collecting data of all kinds and combining and then querying it after- ward. Figure 3.8 shows a screenshot of the sample page while it is collecting details of orders for all customers whose ID starts with m and building up the DataSet instance. 3 Loading Progress and Status Displays 92 Flushing Intermediate Content to the Client Of course, if the process has to access several different data sources to generate the result- ing page, as is most likely the case with the MSN Expedia example mentioned earlier in this chapter, you can flush the individual chunks of “status” content to the browser in between opening each connection, extracting the data, and closing it again. 05 0672326744 CH03 5/4/04 12:25 PM Page 92 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 93 Implementing a Staged Page Load Process You’ll learn about this page in more detail shortly, but first you need to see how you can pass status and other information back to the XMLHTTP object. Then you’ll see how the opera- tion page, which collects the data and stores it in the user’s session, works. After that, you’ll see how the main page calls this opera- tion page and how it displays the status infor- mation and results. Status Information in ASP.NET and the XMLHTTP Object When a browser or any other client (such as XMLHTTP ) requests an HTML page, the server Send Request Update Status Display Send Request Update Status Display Send Request Update Status Display Send Request Update Status Display Display Order Total Display Orders List XMLHTTP XMLHTTP XMLHTTP XMLHTTP stagedloading.aspx stagedfetchpage.aspx Add Customers Add Orders Add Details Calculate Total DataSet in ASP.NET Session Database FIGURE 3.7 A flowchart of the steps in implementing a staged page load process. FIGURE 3.8 The staged processing and reporting sample page in action. Accessing Physically or Geographically Separated Data Sources The set of steps used in this example could easily be performed in one pass. However, using separate stages demonstrates how you could in a more complex scenario access multiple different data sources that could be physically and geographically separated. These data sources might be Web services, XML documents, or other types of data sources—and not just relational databases. For instance, take the MSN Expedia example mentioned earlier: It’s likely that the data sources being accessed would be hosted by different airlines, hotels, rental car compa- nies, and so on. 05 0672326744 CH03 5/4/04 12:25 PM Page 93 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. returns an HTTP status header, followed by the page that was requested. If there is no error (that is, the page can be found and executed by the server), it returns the status header “200 OK” . However, even if the process of loading and executing the page succeeds, you can still control the status code that is returned by setting the Status , StatusCode , and/or StatusDescription prop- erties of the current ASP.NET Response object. The values of these properties will be exposed by the status and statusText properties of the XMLHTTP object after it loads the page (see Table 3.2). You can find a full list of the standard HTTP status codes at www.w3.org/Protocols/rfc2616/ rfc2616-sec10.html . TABLE 3.2 The Equivalent Status-Related Properties of the ASP.NET Response and XMLHTTP Objects ASP.NET Response Object Property XMLHTTP Object Property Description Status No direct equivalent A combination of the status code and status descrip- tion (for example, “200 OK” or “302 Object Moved” ) StatusCode status The numeric part of the status information (for example, 200 or 302 ) StatusDescription statusText The text or description part of the status information (for example, “OK” or “Object Moved” ) By default, the server will automatically set the ASP.NET Status property to “200 OK” if there is no error or to the standard HTTP status code for any error that does occur (for example, “500 Internal Server Error” if there is an ASP.NET code execution error). However, if you trap ASP.NET errors in the code—for example, a failed database connection or a numeric calculation error—you must set the Status property (or the StatusCode and StatusDescription properties) if an error does occur. The Staged Process Operation Page The main page that the user sees makes repeated requests to the operation page ( stagedfetchpage.aspx ), passing the customer ID and the appropriate step number each time. Because it does this by using the XMLHTTP component, the operation page doesn’t have to generate any HTML or output. All it has to do is indicate to the main page whether there was an error or whether this step of process succeeded. However, not all the values you pass back to the XMLHTTP object in this example are strictly status messages; for example, the order value total that is displayed at the end of the process must be returned to the main page. So rather than use the StatusDescription property ( statusText in XMLHTTP ), you can write these messages directly into the page that is returned. The XMLHTTP object can retrieve this as the responseText property, as shown in the previous example. The Page_Load Event Handler for the Staged Loading Example Listing 3.7 shows the Page_Load event handler in the operation page, together with the page-level variable that holds a reference to the DataSet instance stored in the session. The values for the customer ID and the current step are collected from the query string each time the page loads. 3 Loading Progress and Status Displays 94 05 0672326744 CH03 5/4/04 12:25 PM Page 94 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 95 Implementing a Staged Page Load Process LISTING 3.7 The Page_Load Event Handler for the Staged Loading Example Dim oDS As DataSet Sub Page_Load() Dim sCustID As String = Request.QueryString(“custID”) Dim sStep As String = Request.QueryString(“step”) Dim sSelect As String ‘ force current thread to sleep for 3 seconds ‘ to simulate complex code execution Thread.Sleep(3000) Select Case sStep Case “1” oDS = New DataSet() sSelect = “SELECT CustomerID, CompanyName, City, “ _ & “Country, Phone FROM Customers “ _ & “WHERE CustomerID LIKE @CustomerID” AddTable(“Customers”, sCustID, sSelect) Case “2” oDS = CType(Session(“thedata”), DataSet) sSelect = “SELECT OrderID, OrderDate FROM Orders “ _ & “WHERE CustomerID LIKE @CustomerID” AddTable(“Orders”, sCustID, sSelect) Case “3” oDS = CType(Session(“thedata”), DataSet) sSelect = “SELECT [Order Details].OrderID, “ _ & “Products.ProductID, Products.ProductName, “ _ & “[Order Details].Quantity, [Order Details].UnitPrice “ _ & “FROM [Order Details] JOIN Products “ _ & “ON [Order Details].ProductID = Products.ProductID “ _ & “WHERE [Order Details].OrderID IN “ _ & “ (SELECT OrderID FROM Orders “ _ & “ WHERE CustomerID LIKE @CustomerID)” AddTable(“OrderDetails”, sCustID, sSelect) Case “4” oDS = CType(Session(“thedata”), DataSet) CalculateTotal() Case Else Response.Status = “500 Internal Server Error” Response.Write(“Error: Invalid Query String Parameter”) End Select End Sub 05 0672326744 CH03 5/4/04 12:25 PM Page 95 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. Next, to simulate a long process, you force the current thread to sleep for 3 seconds (as you did in the “please wait” example) before using the step value from the query string to decide which action the page will carry out. The first three stages of the operation must create and execute a database query to extract the appropriate set of rows and then add these to the DataSet instance in the user’s session. The AddTable routine, which you’ll see shortly, achieves this. Obviously, you have to a create new DataSet instance at Stage 1, but the remaining stages can extract this DataSet instance from the user’s session. At Stage 4 in this example, the operation page has to calculate the order total and return it to the main page, using the routine CalculateTotal (which you’ll see shortly). Any value greater than 4 for the step parameter is treated as an error, and the page returns the server-side execution error “500 Internal Server Error” . A more detailed error message is also sent back as the content of the returned page. Adding Tables to the DataSet Instance Adding a table to the DataSet instance you extract from the user’s session is simple, and the code in Listing 3.8 demonstrates the traditional techniques you use. Notice that, in this code, you check whether you actually managed to find a DataSet instance in the session, and you return an error status and message if not. After adding the table, you push the updated DataSet instance back into the session. If there is an error while extracting the rows, a suitable error status and message are returned to the user instead. LISTING 3.8 The AddTable Routine for the Staged Loading Example Sub AddTable(sTableName As String, sCustID As String, _ sSelect As String) If oDS Is Nothing Then Response.Status = “500 Internal Server Error” Response.Write(“Error: Cannot access DataSet in session”) Else 3 Loading Progress and Status Displays 96 Accessing the Customer ID Value The value of the customer ID entered into the text box cannot be extracted directly as the Text property of the ASP.NET TextBox control when this page is executed. The page is loaded with the “GET” method by the XMLHTTP object, with the customer ID appended to the query string, so it must be collected from there each time. What Happens if Cookies Are Disabled? The sample page will fail to work properly if the user has cookies disabled in his or her browser because ASP.NET will not be able to maintain a user session. One solution would be to enable cookieless sessions by adding the element <sessionState cookieless= ”true” /> to the <system.web> section of the web.config file for the application. In this case, you must also modify the src attribute of the non–server control <img> elements to specify the full path to the images because the inclusion of the session key in the page URL breaks the links to images that are specified only as relative paths from the URL of the page that hosts them. 05 0672326744 CH03 5/4/04 12:25 PM Page 96 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 97 Implementing a Staged Page Load Process Dim sConnect As String = ConfigurationSettings.AppSettings( _ “NorthwindSqlClientConnectString”) Dim oConnect As New SqlConnection(sConnect) Dim oDA As New SqlDataAdapter(sSelect, oConnect) oDA.SelectCommand.Parameters.Add(“@CustomerID”, sCustID & “%”) Try ‘ fill table in DataSet and put back into session oDA.Fill(oDS, sTableName) Session(“thedata”) = oDS Response.Status = “200 OK” Response.Write(“OK”) Catch oErr As Exception Response.Status = “500 Internal Server Error” Response.Write(“Error: “ & oErr.Message) End Try End If End Sub Calculating the Total Value of the Orders The final section of the operation page in the staged loading example is shown in Listing 3.9. This simply references the OrderDetails table in the DataSet instance and sums the values in each row by multiplying the quantity by the unit price. The result is written back to the response as a fixed-point number with two decimal places. LISTING 3.9 The CalculateTotal Routine for the Staged Loading Example Sub CalculateTotal() Dim dTotal As Decimal = 0 Try For Each oRow As DataRow In oDS.Tables(“OrderDetails”).Rows dTotal += (oRow(“Quantity”) * oRow(“UnitPrice”)) Next Response.Status = “200 OK” LISTING 3.8 Continued 05 0672326744 CH03 5/4/04 12:25 PM Page 97 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. [...]... source and, at first glance, the DataGrid control doesn’t seem to be able to do this Many third-party grid controls are available for ASP.NET that are designed to provide this feature, but it’s quite easy to achieve the same effect with a DataGrid control or a combination of ASP.NET list controls The process requires that the list controls be nested so that each row within the grid that displays the parent... /images/True.gif” /> You use the HTML element here because this is easier to connect to a clientside event handler than the ASP.NET Button element (You don’t have to add the onclick attribute on the server via the Attributes collection.) You always return false from the event handler that is attached to this button because... Do the Check Mark Images Disappear? Notice that the check mark images disappear from the page following the postback that populates the DataSet instance Remember that unlike changes made in server-side ASP.NET code, any changes made to the page displayed in the browser using client-side script are not persisted across postbacks The Show Orders button (refer to Figure 3.9), which appears only after all... construct in the operation page code (refer to Listing 3.8) catches the error It returns the status code “500 Internal Server Error” and the text “Error:”, followed by the error message (as returned by ASP.NET when the data access operation failed) as the content of the page The client-side code then displays the returned page content, as shown in Figure 3.11 FIGURE 3.11 The sample page, reporting a... information messages, you can now look at the main page that calls this operation page at each stage of the overall process Listing 3.10 shows the HTML content of the main page You can see that there is an ASP.NET TextBox control for the user to enter the full or partial customer ID and an element that creates the submit button captioned Calculate LISTING 3.10 The HTML Declarations for the Main Page... nested DataGrid controls is to use syntax that allows the child rows to be specified using declarative techniques In other words, you specify the binding properties of the nested grid at design time, and ASP.NET fetches the rows and performs the binding to generate the output at runtime The sample page in Figure 4.1 shows nested binding of three DataGrid controls, displaying data extracted from the Northwind... only when you’re performing a postback However, if you place a Web Forms control such as a TextBox control on the page—perhaps to allow editing of the contents—you must use a server-side tag Most ASP.NET development tools insert a server-side tag into every page by default 111 112 4 Working with Nested List Controls errors The declaration of the DataGrid control includes a range of style... control The OnItemDataBound attribute specifies the name of an event handler that the DataGrid control will execute each time it binds to the source data for a row In this event handler, you can access the ASP.NET server controls in the current row and the row in the source row set that is providing the data for the row The opening tag of the root DataGrid control (id=”dgr1”) looks like this: . www.w3.org/Protocols/rfc2 616 / rfc2 616 -sec10.html . TABLE 3.2 The Equivalent Status-Related Properties of the ASP. NET Response and XMLHTTP Objects ASP. NET Response Object. Status Displays 10 0 05 0672326744 CH03 5/4/04 12 :25 PM Page 10 0 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 10 1 Implementing