The WebRequest object works with a static WebRequestManager JavaScript object to coordinate the behavior of the ASP.NET AJAX networking stack. Even though you may not reference the object directly, the individual WebRequest objects that you create work with it to execute requests using the XMLHttpRequest object. In fact, the WebRequestManager does not know the specifics of the XMLHttpRequest object. Instead, it works with the WebRequestExecutor, an object which abstracts some of the underlying browser-specific details. These JavaScript objects are part of the Microsoft AJAX Library. Later in this chapter, you will learn how to use a WebRequestExecutor tailored for working with XML data instead of JSON.
Listing 6-10 (TimedSleep.aspx) modifies the Sleep.aspx page to return the amount of time that has passed during request processing instead of specific form variables. The sleep of two seconds simulates a server-side process that may require two seconds to execute.
<%@ Page Language="C#" %>
<script runat="server">
protected override void OnLoad(EventArgs
{
base.OnLoad(e);
DateTime start = DateTime.Now;
System.Threading.Thread.Sleep(2000);
DateTime finish = DateTime.Now;
Response.Write(finish - start);
}
</script>
Listing 6-11 (CallTimedSleep.aspx) does not specify a timeout, so it gets the default value of 0, which indicates that the WebRequest should be allowed to wait indefinitely for a response from the server.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Networking</title>
<script type="text/javascript">
var numberOfTries = 0;
function pageLoad() {
var webRequest = new Sys.Net.WebRequest();
webRequest.set_url('TimedSleep.aspx');
webRequest.add_completed(completedHandler);
webRequest.invoke();
}
function completedHandler(result, eventArgs) {
if(result.get_responseAvailable()) {
$get('placeholder').innerText = "time taken = " +
result.get_responseData();
}
}
</script>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager runat="server" ID="ScriptManager1">
</asp:ScriptManager>
<div id="placeholder">processing request</div><br />
</form>
</body>
</html>
The result of requesting the page using Internet Explorer and Firefox is shown in Figure 6-3.
Instead of explicitly setting the timeout on each WebRequest object created in the page, the default timeout for all web requests can be set on the WebRequestManager. Listing 6-12 (SetDefaultTimeout.aspx) shows the modified pageLoad and completion handler code to demonstrate this. The default timeout is set to one second, but the page is going to sleep for two, so it will always fail. In the completion event, the timeout condition is detected and a new request is created. It will also have the one-second timeout given to the WebRequestManager.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Networking</title> <script type="text/javascript"> var numberOfTries = 0; function pageLoad() { Sys.Net.WebRequestManager.set_defaultTimeout(1000); var webRequest = new Sys.Net.WebRequest(); webRequest.set_url('TimedSleep.aspx'); webRequest.add_completed(completedHandler); webRequest.invoke(); } function completedHandler(result, eventArgs) { if(result.get_timedOut()) { $get('placeholder').innerText = "Timeout. Trying again after " + numberOfTries + " times"; numberOfTries = numberOfTries + 1; var anotherWebRequest = new Sys.Net.WebRequest(); anotherWebRequest.set_url('TimedSleep.aspx'); anotherWebRequest.add_completed(completedHandler); anotherWebRequest.invoke(); } if(result.get_responseAvailable()) { $get('placeholder').innerHTML = "time taken = " + result.get_responseData(); } } </script> </head> <body> <form id="form1" runat="server"> <asp:ScriptManager runat="server" ID="ScriptManager1"> </asp:ScriptManager> <div id="placeholder">processing request</div><br /> </form> </body> </html>
Just as you may want to set a timeout to apply to all web requests, you may also want to provide a single function as the completion handler for all requests. In previous examples, the add_completed method was called on the individual WebRequest, but you can see that this would become repetitious when defining a set of request objects that all use the same target.
The WebRequestManager has the add_completedRequest and remove_completedRequest methods for attaching and detaching event handlers to be used for all requests. Listing 6-13 (GlobalCompleted.aspx)is a page that calls the time.aspx and TimedSleep.aspx pages and uses a single handler for both WebRequest instances.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Networking</title> <script type="text/javascript"> function pageLoad() { Sys.Net.WebRequestManager.add_completedRequest(completedHandler); var webRequest = new Sys.Net.WebRequest(); webRequest.set_url('time.aspx'); var webRequest2 = new Sys.Net.WebRequest(); webRequest2.set_url('TimedSleep.aspx'); Sys.Net.WebRequestManager.executeRequest(webRequest2); Sys.Net.WebRequestManager.executeRequest(webRequest); } function completedHandler(result) { if(result.get_responseAvailable()) { $get('placeholder').innerHTML += "<br />" result.get_webRequest().get_url() + " returned " + result.get_responseData (); } } </script> </head> <body> <form id="form1" runat="server"> <asp:ScriptManager runat="server" ID="ScriptManager1"> </asp:ScriptManager> <div id="placeholder">processing request</div><br /> </form> </body> </html>
Because this code just displays the results of the call, it has a single completion handler, registered with the WebRequestManager. When you call or invoke a WebRequest, it ultimately gets processed by the WebRequestManager’s executeRequest method. You can also call the executeRequest method directly, as shown in Listing 6-13.
Along with the data returned from executing the web request, the URL of the page is also displayed. The completion handler receives an argument of the type NetworkRequestEventArgs, which has a get_webRequest method. You then have access to the original WebRequest object.
The WebRequestManager also allows you to register event handlers to be called for each request as it is about to be called:
Sys.Net.WebRequestManager.add_invokingRequest(invokingHandler);
The CancelEventArgs passed to the invokingRequest handler allow you to cancel the request before it is actually executed.
function invokingHandler(sender, eventArgs) { if(eventArgs.get_webRequest().get_url() === 'TimedSleep.aspx') eventArgs.set_cancel(true); } }
You can attach multiple event handlers for the invoking and completed events, and they will all be called. Even if one of the invoking event handlers cancels the request, the rest of the invoking handlers will still be called, so you have to be careful. And if you attach a completion handler directly to a WebRequest in addition to using the WebRequestManager, they will both be called.
The WebRequestManager works with WebRequest objects to process requests of using an executor object. You saw examples of using this to issue requests for .aspx pages and .asmx web services that are only returning text. But a lot of message passing on the web utilizes XML data for increased fidelity, flexibility, and portability. The default executor used by the WebRequestManager is XMLHttpExecutor. The WebRequestManager includes functions to get and set which default executor you want it to use:
Sys.Net.WebRequestManager.get_defaultExecutorType(); Sys.Net.WebRequestManager.set_defaultExecutorType(Sys.Net.XMLHttpExecutor);
In addition to the WebRequestExecutor, ASP.NET AJAX includes an XMLHttpExecutor that will parse the XML results of a request for you. To demonstrate this process, Listing 6-14 (XMLTime.aspx) is a modification of the earlier page that returns the time from the server, but now the result is packaged as XML.
<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
Response.Write("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
Response.Write("<time><universalTime>" +
DateTime.Now.ToUniversalTime() +
"</universalTime></time>");
}
</script>
When the XMLHttpExecutor is used, the results will be parsed as XML and the XML will be available for use in the DOM. The default executor type is the XMLHttpExecutor, but I am creating it explicitly in Listing 6-15 (XMLHttpExecutor.aspx) to show that you can call the executeRequest method on it directly.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Networking</title> <script type="text/javascript"> function pageLoad() { Sys.Net.WebRequestManager.add_completedRequest(completedHandler); var webRequest = new Sys.Net.WebRequest(); webRequest.set_url('XMLTime.aspx'); var executor = new Sys.Net.XMLHttpExecutor(); webRequest.set_executor(executor); executor.executeRequest(); } function completedHandler(result) { if(result.get_responseAvailable()) { alert(result.get_xml().xml); } } </script> </head> <body> <form id="form1" runat="server"> <asp:ScriptManager runat="server" ID="ScriptManager1"> </asp:ScriptManager> </form> </body> </html>