1. Trang chủ
  2. » Công Nghệ Thông Tin

ASP.NET AJAX Programmer’s Reference with ASP.NET 2.0 or ASP.NET 3.5 phần 5 pdf

156 350 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 156
Dung lượng 595,51 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

ResolveRequestCache : The current HttpApplication object fires the ResolveRequestCache event to enable interested HTTP modules and application code to service the current request from th

Trang 1

If both of the conditions are met, this indicates that the client has made the current request to invoke a server method that resides on an aspx file, and consequently, the Init method takes the following steps:

1 It invokes the CreateHandler static method on the RestHandler to create a RestHandler HTTP handler:

IHttpHandler restHandler = RestHandler.CreateHandler(application.Context);

As previously discussed, the RestHandler HTTP handler knows how to process REST method call requests

2 It calls the ProcessRequest method on this RestHandler HTTP handler to process the current REST method call request:

restHandler.ProcessRequest(application.Context);

As discussed earlier, this method invokes the server method

3 It calls the CompleteRequest method on the current HttpApplication object to complete and shortcut the request, and return the response to the client:

application.CompleteRequest();

}

As you can see, the current request does not go any further down the ASP.NET request processing pipeline To understand the significance of this shortcut, you need to take a look at a normal ASP.NET request processing pipeline where a normal ASP.NET request goes all the way down this pipeline, which consists of the following steps:

1 BeginRequest : The current HttpApplication object raises the BeginRequest event when it begins processing the current request An HTTP module can register an event handler for this event to perform tasks that must be performed at the beginning of the request For example, this

is a good place for an HTTP module to perform URL rewriting

2 AuthenticateRequest : The current HttpApplication object raises the AuthenticateRequest event to enable interested HTTP modules and application code to authenticate the current request

3 PostAuthenticateRequest : The current HttpApplication object fires the

PostAuthenticateRequest event after the request is authenticated An HTTP module can register an event handler for this event to perform tasks that must be performed after the current request is authenticated

4 AuthorizeRequest : The current HttpApplication object fires the AuthorizeRequest event

to enable interested HTTP modules and application code to authorize the current request

5 PostAuthorizeRequest : The current HttpApplication object fires the PostAuthorizeRequest event after the request is authorized An HTTP module can register an event handler for this event

to perform tasks that must be performed after the current request is authorized

6 ResolveRequestCache : The current HttpApplication object fires the ResolveRequestCache event to enable interested HTTP modules and application code to service the current request from the cache, bypassing the rest of the request processing pipeline to improve the performance

of the application

Trang 2

7 PostResolveRequestCache : If the response for the current request has not been cached

(because the current request is the first request to the specified resource for example), the current

HttpApplication object fires the PostResolveRequestCache event An HTTP module can

register an event handler for this event to perform tasks that must be performed after the search

in the cache fails

8 PostMapRequestHandler : The current HttpApplication object fires the

PostMapRequestHandler event after it has been detemined what type of HTTP handler must

handle the current request An HTTP module can register an event handler for this event to

per-form tasks that must be perper-formed after the type of HTTP handler is specified

9 AcquireRequestState : The current HttpApplication object fires the AcquireRequestState

event to enable interested HTTP modules and application code to acquire the request state from

the underlying data store

10 PostAcquireRequestState : The current HttpApplication object fires the

PostAcquireRequestState event after the request state is acquired to enable interested

HTTP modules and application code to perform tasks that must be performed after the request

state is acquired

11 PreRequestHandlerExecute : The current HttpApplication object fires the

PreReqeustHandlerExecute event before executing the HTTP handler responsible for

handling the current request An HTTP module can register an event handler for this event to

perform tasks that must be performed right before the ProcessRequest method of the HTTP

handler is invoked to execute the handler

12 PostRequestHandlerExecute : The current HttpApplication object fires the

PostRequestHandlerExecute event after the ProcessRequest method of the HTTP

handler returns, signifying that the HTTP handler responsible for handling the current request

has been executed

13 ReleaseRequestState : The current HttpApplication object fires the ReleaseRequestState

event to enable interested HTTP modules to release or store the request state into the underlying

data store

14 PostReleaseRequestState : The current HttpApplication object fires the

PostReleaseRequestState event right after the request state is stored into the underlying

data store to enable the interested HTTP modules and application code to run logic that must be

run after the request state is saved

15 UpdateRequestCache : The current HttpApplication object fires the UpdateRequestCache

event to enable interested HTTP modules to cache the current response in the ASP.NET cache

16 PostUpdateRequestCache : The current HttpApplication object fires the

PostUpdateRequestCache event after the current response is cached in the ASP.NET

Cache object

17 EndRequest : The current HttpApplication object fires the EndRequest event after the current

response is sent to the client to mark the end of processing the current request

As discussed earlier, the ScriptModule kicks in when the current HttpApplication fires its

PostAcquireRequestState event As you can see in Listing 14-29 , the ScriptModule ’s event handler

for this event invokes the CompleteRequest method on the current HttpApplication object to force

this object to bypass the rest of the events and directly raise the last event: EndRequest

Trang 3

To see this in action, follow these steps:

1 Create an AJAX-enabled Web site in Visual Studio

2 Add a Global.asax file to the root directory of this Web site

3 Add the code shown in Listing 14-30 to the Global.asax file

4 Add a breakpoint to each method in the Global.asax file

5 Add a Web form ( aspx file) to this Web site

6 Add the code previously shown in Listing 14-17 to the .aspx file created in step 5

7 Press F5 to run the Web site in debug mode

The debugger stops at every breakpoint in the Global.asax file, in top-to-bottom order This signifies two things First, the first request goes through the entire ASP.NET request processing pipeline Second, the current HttpApplication raises its events in the order discussed earlier

8 When the Web page appears, enter two numbers in the specified text boxes and press F5 to run the Web site in debug mode again

The debugger jumps from the breakpoint in the Application_AcquireRequestState method directly to the breakpoint in the Application_EndRequest method This clearly shows that the current request goes through only the first 10 steps of the pipeline, skipping the last eight steps Listing 14-30: The Global.asax File

<%@ Application Language=”C#” %>

<script RunAt=”server”>

void Application_BeginRequest(object sender, EventArgs e) { } void Application_AuthenticateRequest(object sender, EventArgs e) { } void Application_PostAuthenticateRequest(object sender, EventArgs e){ } void Application_AuthorizeRequest(object sender, EventArgs e) { } void Application_PostAuthorizeRequest(object sender, EventArgs e) { } void Application_ResolveRequestCache(object sender, EventArgs e) { } void Application_PostResolveRequestCache(object sender, EventArgs e) { } void Application_PostMapRequestHandler(object sender, EventArgs e) { } void Application_AcquireRequestState(object sender, EventArgs e) { } void Application_PostAcquireRequestState(object sender, EventArgs e) { } void Application_PreRequestHandlerExecute(object sender, EventArgs e) { } void Application_PostRequestHandlerExecute(object sender, EventArgs e) { } void Application_ReleaseRequestState(object sender, EventArgs e) { } void Application_PostReleaseRequestState(object sender, EventArgs e) { } void Application_UpdateRequestCache(object sender, EventArgs e) { } void Application_PostUpdateRequestCache(object sender, EventArgs e) { } void Application_EndRequest(object sender, EventArgs e) { }

</script>

If you decide to allow your client-side code to asynchronously invoke a server-side method that belongs

to a Web page in your application, you must keep the following in mind:

Trang 4

❑ None of the event handlers registered for PostAcquireRequestState ,

PreRequestHandlerExecute , ReleaseRequestState , PostReleaseRequestState ,

UpdateRequestCache , and PostUpdateRequestCache will be invoked

❑ The request state, such as Session data, will not be stored in the underlying data store

because the request skips the ReleaseRequestState step during which the request state is

stored in the data store This means that none of the changes made to the session data will

be stored in the data store, and therefore, they will be lost at the end of the current request

❑ The server response will not be cached in the ASP.NET Cache object because the current request

skips the UpdateRequestCache step

Due to these fundamental limitations, the ASP.NET AJAX framework requires the server-side method to

be static

As previously shown in Listing 14-29 , the ScriptModule hands the request over to the RestHandler

HTTP handler if the current request is a REST request In other words, after the ScriptModule kicks in,

the Page is no longer the HTTP handler responsible for processing the current request The ScriptModule

delegates this responsibility from Page to the RestHandler HTTP handler, and consequently, the

ProcessRequest method of the RestHandler HTTP handler (not Page ) is invoked This has significant

consequences The ProcessRequest method of the Page class starts what is known as the Page lifecycle

This means that the Page does not go through its lifecycle phases when the current request is a REST

method call request Therefore, you cannot access any of the server controls on the current page This is yet

another reason why the server-side method must be static

Web Services Bridges Demystified

As the implementation of the RestHandler class’s CreateHandler static method clearly shows, this

method assumes that the method being invoked is annotated with the WebMethodAttribute metadata

attribute In other words, the RestHandler HTTP handler assumes that the method being invoked is

always a Web method

How does the RestHandler HTTP handler process requests for an asbx file given the fact that this file

has nothing to do with Web services? To find the answer to this question, you need to revisit the

imple-mentation of the CreateHandler static method, which is shown again in Listing 14-31 As the

high-lighted portion of this listing shows, the CreateHandler method invokes the GetCompiledType static

method on the BuildManager class This method parses and compiles the file with the specified virtual

path into a dynamically generated class

Listing 14-31: The CreateHandler Static Method Revisited

internal static IHttpHandler CreateHandler(HttpContext context)

{

string servicePath = context.Request.FilePath;

Type serviceType = BuildManager.GetCompiledType(servicePath);

}

Now the question is: What type of class does the GetCompiledType method generate for an asbx file?

To find the answer to this question, run Listing 14-20 in debug mode The client code contained in this

Trang 5

listing makes a REST request to the server to invoke the Divide method of the Math class This request is made for a file with extension asbx that describes the class and method

After running Listing 14-20 , go to the following directory on your machine (or, if you have installed NET framework in a different directory than the following standard directory, go to that directory):

%windir%\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files

In this directory, search for the directory with the same name as your application Then go down to a different directory, and search for a source file with a name that has the following format :

App_Web_math.asbx.23fc0e6b.kgwm5mhb.0.cs

Note that the name of this source file begins with App_Web_ , followed by the name of the asbx file (which is math.asbx in this case), followed by some randomly generated hash values to ensure the uniqueness of the file name

If you open this file in your favorite editor, you should see the code shown in Listing 14-32 (which has been cleaned up for presentation purposes)

Listing 14-32: The Dynamically Generated Code for the Web Service that Wraps

a Custom Class

namespace MyNamespace{

this.VirtualPath = “/AJAXFuturesEnabledWebSite2/NewFolder1/Math.asbx”;

this.BridgeXml = @”<?xml version=””1.0”” encoding=””utf-8”” ?>

<bridge namespace=””MyNamespace”” className=””MyMath””>

<proxy type=””CustomComponents.Math, App_Code””/>

Trang 6

public override object CallServiceClassMethod(string method,

Dictionary<string, object> args,

throw new ArgumentException(“Argument not found: x”);

double arg0 = ((double)(BridgeHandler.ConvertToType(obj, typeof(double))));

if (args.TryGetValue(“y”, out obj)) { }

else

throw new ArgumentException(“Argument not found: y”);

double arg1 = ((double)(BridgeHandler.ConvertToType(obj, typeof(double))));

return proxy.Divide(arg0, arg1);

This code defines a class with the name specified in the className attribute on the bridge document

element The document element also belongs to a namespace with the name specified in the namespace

attribute on this element Note that this class exposes a string property named BridgeXml that contains

the contents of the asbx file

Trang 7

As you can see in the code listing, this class is annotated with the WebServiceAttribute metadata attribute, which means that this class is a Web service Therefore, the call into the BuildManager class’s

GetCompiledType static method that was previously highlighted in Listing 14-31 creates a Web service under the hood with the name specified in the className attribute on the bridge document element

This exposes a Web method with the name specified in the name attribute on the <method> element of the asbx file The ASP.NET AJAX Web services bridge simply creates a Web service wrapper around your custom class and exposes its methods as Web methods

Considering the fact that the GetCompiledType static method of the BuildManager class takes only the virtual path of the file being compiled, and has no knowledge of the type of file it is dealing with, how does this method know what type of class to generate? The answer is it doesn’t Under the hood, the

GetCompiledType method delegates the responsibility of parsing the file and generating the code for the

class that represents the file to another component known as a build provider Each type of build provider is

specifically designed to parse and generate code for a file with specific extension The following table presents

a few examples of build providers and the file extension for which each build provider generates code

As shown in this table, the ASP.NET framework includes a build provider named BridgeBuildProvider , which is specifically designed to parse and generate code for files with extension asbx This build pro-vider is the one that generates the code for the Web service wrapper shown in Listing 14-32 If you check out the web.config file in your AJAX-enabled Web site, you’ll see the following code, which registers the

BridgeBuildProvider with the ASP.NET compilation infrastructure:

Using the Replicas

The previous sections provided you with the complete replica implementations of the following main components of the ASP.NET AJAX REST method call request processing infrastructure:

❑ ScriptHandlerFactory ❑ RestHandlerFactory

Build Provider File Type

Trang 8

❑ RestHandler

❑ HandlerWrapper

❑ ScriptModule

As mentioned earlier, these replicas are fully functional Follow these steps to see the replicas in action:

1 Create an AJAX-enabled Web site in Visual Studio

2 Add an App_Code directory in this Web site

3 Add a new source file named ScriptHandlerFactory.cs to the App_Code directory, and then

add the code shown in Listing 14-22 to this source file

4 Add a new source file named RestHandlerFactory.cs to the App_Code directory, and then

add the code shown in Listing 14-24 to this source file Comment out the following two lines of

code from this source file to remove the reference to the RestClientProxyHandler (which

hasn’t been covered yet):

//if (IsClientProxyRequest(context.Request.PathInfo))

// return new RestClientProxyHandler();

5 Add a new source file named RestHandler.cs to the App_Code directory, and then add the

code shown in Listing 14-25 to this source file

6 Add a new source file named HandlerWrapper.cs to the App_Code directory, and then add the

code shown in Listing 14-26 to this source file

7 Add a new source file named ScriptModule.cs to the App_Code directory, and then add the

code shown in Listing 14-29 to this source file

8 Add a new Web form ( .aspx file) named PageMethods.aspx , and then add the code shown in

Listing 14-17 to this aspx file

9 Add a new Web form ( .aspx file) named Math.aspx to the root directory of this Web site, and

then add the code shown in Listing 14-18 to this aspx file

10 Add a new XML file named Math.asbx to the root directory of this Web site, and then add the

XML document shown in Listing 14-19

11 Add a new source file named Math.cs to the App_Code directory, and then add the code shown

in Listing 14-18 to this source file

12 Add a new Web form ( .aspx file) named Math2.aspx to the root directory of this Web site, and

then add the code shown in Listing 14-15 to this aspx file

13 Add a new Web service ( .asmx ) named Math.asmx to the root directory of this Web site, and

then add the code shown in Listing 14-16 to this asmx file

14 In the web.config file, comment out the italicized lines shown in the following code, and add

the boldface portion of the code (which is basically replacing the standard ASP.NET

Scrip-tHandlerFactory and ScriptModule with the replica ScriptHandlerFactory and

ScriptModule ):

Trang 9

Summar y

This chapter provided you with in-depth coverage of the ASP.NET AJAX REST method call request’s processing infrastructure It introduced Web services bridges, which are covered in more detail in Chapter 19 , where you’ll learn how to develop a custom script server control that uses a bridge to enable the client code to interact with Amazon Web services

The next chapter builds on what you learned in this chapter to show you how this infrastructure manages to hide its complexity behind proxy classes

Trang 11

Proxy Classes

The previous chapter provided you with in-depth coverage of the ASP.NET AJAX REST method call request processing infrastructure This chapter shows you how this infrastructure hides its complexity behind proxy classes to enable you to program against a remote object as you would against a local object

What’s a Proxy, Anyway?

Let’s revisit the add JavaScript function shown in Listing 14-14 of Chapter 14 , and shown again here in Listing 15-1 This JavaScript function was registered as the event handler for the Add but-ton’s click event of in Listing 14-14

Listing 15-1: The add Method

function add() {

var servicePath = “ http://localhost/AJAXEnabledFuturesWebSite2/Math.asmx”;

var methodName = “Add”;

var useGet = false;

var xValue = $get(“firstNumber”).value;

var yValue = $get(“secondNumber”).value;

var params = {x : xValue, y : yValue};

var userContext = $get(“result”);

var webServiceProxy = new Sys.Net.WebServiceProxy();

Trang 12

Listing 15-2: A Local Class with the Same Name and Methods as the Remote

Wouldn’t your implementation of the add method be something like the one shown in Listing 15-3 ?

Listing 15-3: Implementation of add Method if the Web Service Class Were a Local Class

function add()

{

var math = new MyNamespace.Math();

var xValue = $get(“firstNumber”).value;

var yValue = $get(“secondNumber”).value;

var z = math.Add(Number.parseInvariant(xValue),

Number.parseInvariant(yValue));

$get(“result”).innerText = z;

}

As you can see in this listing, if the Math object were a local object, you would directly invoke the

Add method on the object and directly pass the x and y values into the Add method itself

However, when the Math object becomes a Web service, it is a remote object, so you cannot directly

invoke the Add method on it, nor can you directly pass the x and y values into it When you’re calling

the Add method on the remote Math object:

❑ You have to worry about the service path where the remote Math object is located:

var servicePath = “http://localhost/AJAXEnabledFuturesWebSite2/Math.asmx”;

You don’t have to worry about the location of a local Math object, because it always resides in

the same address space as the rest of your program and, consequently, you have direct access

to the object

You have to pass the name of the method as a string into the _invoke method of the

WebServiceProxy object:

var methodName = “Add”;

request = webServiceProxy._invoke(servicePath, methodName, useGet, params,

onSuccess, onFailure, userContext);

Trang 13

This is obviously very different from a local method invocation where you directly invoke the method on the object instead of passing a string around

You have to pass the names and values of the parameters of the method as a dictionary into the

_invoke method of the WebServiceProxy object

var params = {x : xValue, y : yValue};

request = webServiceProxy._invoke(servicePath, methodName, useGet, params,

onSuccess, onFailure, userContext);

This is obviously very different from a local method invocation where you directly pass these parameters into the method itself instead of passing a dictionary around

❑ You’re trying to call a method named Add , which takes two parameters of type double and returns a value of type double , on an object of type Math , but you have to call a method with a different name ( _invoke ), with completely different parameters ( servicePath , methodName , useGet ,

params , onSucess , onFailure , and userContext ), and with a completely different return type ( WebRequest ), on a completely different object (the WebServiceProxy object):

request = webServiceProxy._invoke(servicePath, methodName, useGet, params, onSuccess, onFailure, userContext);

This is obviously very different from a local method invocation where you directly invoke the

Add method on the Math object

As you can see, invoking the Add method on the remote Math object doesn’t look anything like invoking the Add method on a local Math object

Proxy Class

The ASP.NET AJAX framework provides you with a local object that has the following characteristics:

❑ It has the same name as the remote object For example, in the case of the Math Web service, the AJAX framework provides you with a local object named Math

❑ It exposes methods with the same names as the methods of the remote object For example, the local

Math object associated with the remote Math Web service object exposes a method named Add ❑ Its methods take parameters with the same names as the associated methods of the remote object For example, the Add method of the local Math object associated with the remote Math Web service object also takes two parameters with the same names as the parameters of the Add method of the remote Math Web service object: x and y

❑ Its methods take parameters of the same types as the associated methods of the remote object For example, the Add method of the local Math object associated with the remote Math Web ser-vice object also takes two parameters of the same types as the parameters of the Add method of the remote Math Web service object: double

❑ Its methods return values of the same types as the associated methods of the remote object For example, the Add method of the local Math object associated with the remote Math Web service object returns a value of the same type as the return value of the Add method of the remote Math Web service object: double

Trang 14

This enables you to program against the local object, instead of the WebServiceProxy object as you did

in the last chapter, and consequently, makes programming against remote objects more like programming

against local objects Because this local object makes it feel like you’re directly interacting with the remote

object, it is known as a proxy object In other words, this local object acts as a proxy for the remote object

Let’s begin by discussing the implementation of the proxy class and object that the ASP.NET AJAX

framework automatically generates for you (The mechanism that actually generates this proxy class and

object is discussed later in the chapter.)

As you learned in the previous chapter, there are three types of remote method invocations:

❑ Invoking a Web method that is part of a Web service

❑ Invoking a page method that is part of an ASP.NET page

❑ Invoking a method that is part of a custom class

Therefore, there are three types of proxy classes:

❑ Proxy classes associated with Web services

❑ Proxy classes associated with page methods

❑ Proxy classes associated with custom classes

Proxy Classes Associated with Web Services

Listing 15-4 presents the implementation of the local Math proxy class associated with the remote Math

Web service class

Listing 15-4: The Local Math Proxy Class Associated with the Remote Math

Web Service Class

var servicePath = MyNamespace.Math.get_path();

var methodName = ‘Add’;

var useGet = false;

var params = {x : x, y : y};

var onSuccess = succeededCallback;

Trang 15

var onFailure = failedCallback;

return this._invoke(servicePath, methodName, useGet, params, onSuccess,onFailure, userContext);

}}

MyNamespace.Math.registerClass(‘MyNamespace.Math’, Sys.Net.WebServiceProxy);

MyNamespace.Math._staticInstance = new MyNamespace.Math();

MyNamespace.Math.set_path = function(value){

MyNamespace.Math._staticInstance._path = value;

}MyNamespace.Math.get_path = function(){

return MyNamespace.Math._staticInstance._path;

}MyNamespace.Math.set_timeout = function(value){

MyNamespace.Math._staticInstance._timeout = value;

}MyNamespace.Math.get_timeout = function(){

return MyNamespace.Math._staticInstance._timeout;

}MyNamespace.Math.set_defaultUserContext = function(value){

MyNamespace.Math._staticInstance._userContext = value;

}MyNamespace.Math.get_defaultUserContext = function(){

return MyNamespace.Math._staticInstance._userContext;

}MyNamespace.Math.set_defaultSucceededCallback = function(value){

MyNamespace.Math._staticInstance._succeeded = value;

}MyNamespace.Math.get_defaultSucceededCallback = function(){

return MyNamespace.Math._staticInstance._succeeded;

}MyNamespace.Math.set_defaultFailedCallback = function(value){

MyNamespace.Math._staticInstance._failed = value;

}

(continued)

Trang 16

All ASP.NET AJAX proxy classes directly or indirectly derive from the Sys.Net.WebServiceProxy class

As you can see from in the following excerpt from Listing 15-4 , this local Math proxy class exposes a

method with the same name as the remote Web service class — Add This method takes parameters with

the same names and types as the remote Web service class’s Add method parameters, and returns a value

of the same type as the remote Web service class’s Add method return value:

MyNamespace.Math.prototype =

{

Add : function(x, y, succeededCallback, failedCallback, userContext)

{

var servicePath = MyNamespace.Math.get_path();

var methodName = ‘Add’;

var useGet = false;

var params = {x : x, y : y};

var onSuccess = succeededCallback;

var onFailure = failedCallback;

return this._invoke(servicePath, methodName, useGet, params,

onSuccess,onFailure, userContext);

}

}

The Add method of this local Math proxy class encapsulates the code that you would otherwise have

to write to interact with the WebServiceProxy object as you did in the previous chapter Note that

Listing 15-4 instantiates an instance of this local Math proxy class and assigns the instance to a private

static field on this class named _staticInstance :

MyNamespace.Math._staticInstance = new MyNamespace.Math();

Trang 17

The code then defines static getters and setters that delegate to the associated getters and setters of this static Math proxy instance Also note that the code exposes a static method named Add on this local Math proxy class, which delegates to the Add method of the static Math proxy instance:

MyNamespace.Math.Add = function(x, y, onSuccess, onFailed, userContext){

MyNamespace.Math._staticInstance.Add(x, y, onSuccess, onFailed, userContext);

MathWebServiceProxy.js , and Listing 15-5 adds a reference to this JavaScript file

Now let’s walk through the implementation of the add JavaScript function shown in the following excerpt from Listing 15-5 :

function add() {

var userContext = $get(“result”);

var xValue = $get(“firstNumber”).value;

var yValue = $get(“secondNumber”).value;

MyNamespace.Math.Add(xValue, yValue, onSuccess, onFailure, userContext);

Listing 15-5: A Page that Uses the Local Static Math Proxy Instance

Trang 18

var userContext = $get(“result”);

var xValue = $get(“firstNumber”).value;

var yValue = $get(“secondNumber”).value;

MyNamespace.Math.Add(xValue, yValue, onSuccess, onFailure, userContext);

}

</script>

</head>

<body>

<form id=”form1” runat=”server”>

<asp:ScriptManager runat=”server” ID=”ScriptManager1”>

Trang 19

Proxy Classes Associated with Page Methods

The following code presents a proxy class associated with a page method named Add , which belongs to the PageMethods.aspx page:

PageMethods = function(){

Add : function(x, y, succeededCallback, failedCallback, userContext) { return this._invoke(PageMethods.get_path(), ‘Add’, false, {x:x, y:y}, succeededCallback, failedCallback, userContext);

}}PageMethods.registerClass(‘PageMethods’, Sys.Net.WebServiceProxy);

(continued)

Trang 21

Comparing this code with Listing 15-4 clearly shows that a proxy class associated with page methods has a fixed named — PageMethods — and does not belong to any namespace All methods annotated with WebMethod metadata attribute on a given page are associated with the same proxy class named

PageMethods on the client side As the boldface portion of the code shows, the set_path method is invoked on the PageMethods proxy object to specify the URL of the PageMethods.aspx page as the target URL for the proxy class

The following code presents a page that uses the PageMethods proxy class As you’ll see later in this chapter, the ASP.NET AJAX framework automatically generates the code for the PageMethods proxy class and adds this code to the current page For now, assume that you generated this code yourself and treat it like any other client script As such, this code is stored in a JavaScript file named

MathPageMethodsProxy.js , and the following code adds a reference to this file Note that the following code is the same as Listing 15-5 , except for the boldface portion where the PageMethods proxy is used to communicate with the underlying page method

//Same as Listing 15-5 }

function add() {

var userContext = $get(“result”);

var xValue = $get(“firstNumber”).value;

var yValue = $get(“secondNumber”).value;

} </script>

</head>

<body>

<form id=”form1” runat=”server”>

<asp:ScriptManager runat=”server” ID=”ScriptManager1”>

Trang 22

Proxy Classes Associated with Custom Classes

The proxy classes associated with custom classes are very similar to the proxy classes associated with

Web services The main difference is that the target URL of the proxy class associated with a custom class

is set to the URL of the asbx file that describes the custom class

Automatic Proxy Class Generation

This illusion will work only if someone implements the Math proxy class for you Otherwise, if you

were to implement this class yourself, it would not be much of an illusion The main challenge with

implementing a proxy class is that one proxy class will not work with all types of remote classes

For example, you cannot use the Math proxy class to talk to the Products Web service class because the

Products Web service class is a completely different Web service class than the Math Web service class

For one thing, the Products Web service class exposes methods such as GetProducts as opposed to

Add This means that you have to use separate proxy classes to talk to different remote classes

This is where the ASP.NET AJAX server-side framework comes to the rescue This framework contains

the logic that automatically generates the code for the proxy class for each remote class that your client

code needs to interact with All you have to do is add a ServiceReference object to the Services

collection of the current ScriptManager server control to specify which remote class you need to talk to

You can do this either imperatively or declaratively

Declarative Approach

Listing 15-6 presents a page that that uses the declarative approach to add a ServiceReference object

to the Services collection of the current ScriptManager server control

Listing 15-6: A Page that Uses the Declarative Approach to Add a ScriptReference Object

Trang 23

var userContext = $get(“result”);

var xValue = $get(“firstNumber”).value;

var yValue = $get(“secondNumber”).value;

MyNamespace.Math.Add(xValue, yValue, onSuccess, onFailure, userContext);

} </script>

</head>

<body>

<form id=”form1” runat=”server”>

<asp:ScriptManager runat=”server” ID=”ScriptManager1”>

Trang 24

As you can see in the boldface portion of this code listing, the page adds an <asp:ServiceReference>

element to the <Services> child element of the <asp:ScriptManager> tag that represents the current

ScriptManager server control on the aspx page Note that this page sets the Path attribute on this

<asp:ServiceReference> tag to the service path of the Math Web service Also note that the page

sets the InlineScript attribute on this tag to true to tell the ASP.NET AJAX server-side framework to

add the definition of the Math proxy class to the markup sent to the requesting browser As a matter of

fact, if you run Listing 15-6 and view the source from your browser, you’ll see Listing 15-7

Listing 15-7: The Source for the Page Shown in Listing 15-6

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”

Trang 25

var userContext = $get(“result”);

var xValue = $get(“firstNumber”).value;

var yValue = $get(“secondNumber”).value;

MyNamespace.Math.Add(xValue, yValue, onSuccess, onFailure, userContext);

} </script>

</head>

<body>

<form name=”form1” method=”post” action=”WebServiceProxy.aspx” id=”form1”>

<div>

<input type=”hidden” name=” EVENTTARGET” id=” EVENTTARGET” value=”” />

<input type=”hidden” name=” EVENTARGUMENT” id=” EVENTARGUMENT” value=”” />

<input type=”hidden” name=” VIEWSTATE” id=” VIEWSTATE”

value=”/wEPDwULLTEzMTg5MjA5NzVkZDZArSkraR3ukOEGxC944PmDWFHr” />

</div>

<script type=”text/javascript”>

var theForm = document.forms[‘form1’];

<! if (!theForm) {

theForm = document.form1;

}function doPostBack(eventTarget, eventArgument) {

if (!theForm.onsubmit || (theForm.onsubmit() != false)) {

theForm. EVENTTARGET.value = eventTarget;

(continued)

Trang 26

var e = Function._validateParams(arguments, [{name: ‘path’, type: String}]); if (e)

throw e; MyNamespace.Math._staticInstance._path = value; }

MyNamespace.Math.get_path = function() { return

MyNamespace.Math._staticInstance._ path; }

MyNamespace.Math.set_timeout = function(value) { var e =

Function._validateParams(arguments, [{name: ‘timeout’, type: Number}]); if (e)

throw e; if (value < 0) { throw Error.argumentOutOfRange(‘value’, value,

Trang 27

MyNamespace.Math.set_defaultSucceededCallback = function(value) { var e = Function._validateParams(arguments, [{name: ‘defaultSucceededCallback’, type: Function}]); if (e) throw e; MyNamespace.Math._staticInstance._succeeded = value; }

MyNamespace.Math.get_defaultSucceededCallback = function() { return MyNamespace.Math._staticInstance._succeeded; } MyNamespace.Math.set_defaultFailedCallback = function(value) { var e = Function._validateParams(arguments, [{name: ‘defaultFailedCallback’, type:

Function}]); if (e) throw e; MyNamespace.Math._staticInstance._failed = value; } MyNamespace.Math.get_defaultFailedCallback = function() {

return MyNamespace.Math._staticInstance._failed; } MyNamespace.Math.set_path(“/AJAXFuturesEnabledWebSite2/Math.asmx”);

MyNamespace.Math.Add= function(x,y,onSuccess,onFailed,userContext) {MyNamespace.Math._staticInstance.Add(x,y,onSuccess,onFailed,userContext); } // >

</script>

<script type=”text/javascript”>

//<![CDATA[

Sys.WebForms.PageRequestManager._initialize(‘ScriptManager1’,document.getElementById(‘form1’));

Trang 28

As you can see, the boldface portion of this listing is just the inline definition of the Math proxy class

This inline solution has the following drawbacks:

❑ Like any other inline solution, it does not allow the browser to cache the same script used in

dif-ferent pages to improve performance For example, if you have multiple pages in your

applica-tion that use the Math proxy class, every single page will include the boldface portion of Listing

15-7 However, if the script that defines the Math proxy class were in a separate file (as shown in

the following code snippet), the browser could download and cache this file once

❑ It increases the size of the page because the definition of the Math proxy class is directly added

to the page As you can see in Listing 15-7 , your pages could get quite large, and the bigger a

page is, the longer it takes to download However, if the boldface portion of Listing 15-7 were in

a separate file (as shown in the following code snippet), the browser would download this script

once and use it across all pages in your application that use the Math proxy class

<asp:ScriptManager runat=”server” ID=”ScriptManager1”>

If you set the InlineScript attribute on the <asp:ScriptReference> to false , run the same page,

and view the source from your browser, you’ll get Listing 15-8

Trang 29

Listing 15-8: The Source of the Page Shown in Listing 15-6 with a ScriptMode Value

var builder = new Sys.StringBuilder();

var userContext = $get(“result”);

var xValue = $get(“firstNumber”).value;

(continued)

Trang 30

Listing 15-8 (continued)

var yValue = $get(“secondNumber”).value;

MyNamespace.Math.Add(xValue, yValue, onSuccess, onFailure, userContext);

<input type=”hidden” name=” EVENTTARGET” id=” EVENTTARGET” value=”” />

<input type=”hidden” name=” EVENTARGUMENT” id=” EVENTARGUMENT” value=”” />

<input type=”hidden” name=” VIEWSTATE” id=” VIEWSTATE”

Trang 32

This script block sets the src to the value /AJAXFuturesEnabledWebSite2/Math.asmx/jsdebug

Note that this value consists of two parts: the first part is the URL of the Math Web service, and the

second part is jsdebug The path trailer jsdebug tells the ASP.NET AJAX server-side framework that

the client code wants to download the debug version of the script If you enter this URL in the address

bar of your browser, you’ll get the JavaScript file that contains the definition of the Math proxy class

(the boldfacportion of Listing 15-7 )

The ScriptManager server control exposes an enumerator property of type ScriptMode and named

ScriptMode This property determines which version of the script to download The following code

presents the definition of the ScriptMode enumerator:

public enum ScriptMode

For example, if you set the ScriptMode attribute on the <asp:ServiceReference> child element

previously shown in Listing 15-6 to Release , run the listing page, and view the page source from your

browser, you’ll see that the source contains the following script block As the boldface portion shows, the

path trailer is now js instead of jsdebug , which means that this time around, the release version of

the script will be downloaded:

type=”text/javascript”> </script>

Imperative Approach

Listing 15-9 presents a page that uses the imperative approach to add a ServiceReference object to the

Services collection of the current ScriptManager server control

Listing 15-9: A Page that Uses the Imperative Approach to Add a ServiceReference

Trang 33

var builder = new Sys.StringBuilder();

var userContext = $get(“result”);

var xValue = $get(“firstNumber”).value;

var yValue = $get(“secondNumber”).value;

MyNamespace.Math.Add(xValue, yValue, onSuccess, onFailure, userContext);

} </script>

</head>

(continued)

Trang 34

Listing 15-9 (continued)

<body>

<form id=”form1” runat=”server”>

<asp:ScriptManager runat=”server” ID=”ScriptManager1”/>

ServiceReference serviceRef = new ServiceReference();

Next, it sets the InlineScript and Path properties of this instance:

serviceRef.InlineScript = false;

serviceRef.Path = “/AJAXFuturesEnabledWebSite2/Math.asmx”;

Finally, it adds the instance to the Services collection of the current ScriptManager server control:

ScriptManager1.Services.Add(serviceRef);

Trang 35

is the user control and the parent page is the page that hosts the user control

The problem in these parent/child page scenarios is that the parent and child are finally merged and form a single page, which means that you cannot include a separate instance of the ScriptManager server control on the parent and child pages As previously discussed, every page can contain only a single instance of the ScriptManager server control

If you put the ScriptManager server control on the parent page, the child page would not be able to add its ServiceReference objects to the Services collection of the ScriptManager server control If you put the ScriptManager server control on the child page, the parent page would not be able to add its ServiceReference objects to the Services collection of the ScriptManager server control

You would have the same problem with ScriptReferences If you put the ScriptManager server control on the parent page, the child page would not be able to add its ScriptReference objects to the

Scripts collection of the ScriptManager server control If you put the ScriptManager server control

on the child page, the parent page would not be able to add its ScriptReference objects to the

Scripts collection of the ScriptManager server control

To tackle these situations, the ASP.NET AJAX framework includes a new server control named

ScriptManagerProxy A child page whose parent page contains an instance of the ScriptManager server control, or a parent page whose child page contains an instance of the ScriptManager server control, can add its ServiceReference and ScriptReference objects to the Services and Scripts collections

of the ScriptManagerProxy server control and rest assured that the ASP.NET AJAX framework will automatically add these ServiceReference and ScriptReference objects to the ScriptManager server control Because the ServiceReference and ScriptReference objects added to the Services and

Scripts collections of the ScriptManagerProxy server control are added to the Services and Scripts collections of the current ScriptManager server control, the ScriptManagerProxy server control acts as a proxy for the current ScriptManager server control

Listing 15-10 contains a user control that employs the ScriptManagerProxy server control

Listing 15-10: A User Control that Employs the ScriptManagerProxy Server Control

<%@ Control Language=”C#” ClassName=”MathUserControl” %>

<script type=”text/javascript” language=”javascript”>

Trang 36

var userContext = $get(“result”);

var xValue = $get(“firstNumber”).value;

var yValue = $get(“secondNumber”).value;

MyNamespace.Math.Add(xValue, yValue, onSuccess, onFailure, userContext);

Trang 37

ScriptManagerProxy server control

Listing 15-11: A Page that Hosts the User Control shown in Listing 15-10

<form id=”form1” runat=”server”>

<asp:ScriptManager runat=”server” ID=”ScriptManager1”/>

<custom:MyUserControl runat=”server” ID=”MyUserControl1” />

</form>

</body>

</html>

Under the Hood

Adding a ServiceReference object to the Services collection of the current ScriptManager server control is all it takes to instruct the ASP.NET AJAX server-side framework to automatically generate a client script that defines, instantiates, and initializes the proxy class To help you understand how the ASP.NET AJAX server-side framework manages to do this, this section implements fully functional

Trang 38

(as far as the discussions in this section are concerned) replicas of the components of the framework that

are responsible for generating a client script that defines, instantiates, and initializes the proxy class

ScriptManager

Listing 15-12 presents the implementation of the replica ScriptManager server control

Listing 15-12: The Replica ScriptManager Server Control

Trang 39

protected override void OnInit(EventArgs e) {

base.OnInit(e);

this.Page.PreRenderComplete += new EventHandler(Page_PreRenderComplete);

} void Page_PreRenderComplete(object sender, EventArgs e) {

if (this._services != null) {

foreach (ServiceReference reference in this._services) {

reference.Register(this);

} }

if (this.EnablePageMethods) {

ClientProxyGenerator generator2 = new ClientProxyGenerator();

string script = generator2.GetClientProxyScript(this.Page.Request.FilePath, true);

this.Page.ClientScript.RegisterClientScriptBlock(typeof(Page), script, script, true);

} } public bool EnablePageMethods {

get { return ViewState[“EnablePageMethods”] != null ? (bool)ViewState[“EnablePageMethods”] : false;

} set { ViewState[“EnablePageMethods”] = value;

} } }}

Services

The replica ScriptManager exposes a collection property of type ServiceReferenceCollection named Services , as defined in the following excerpt from Listing 15-12 Note that this property is annotated with the PersistenceMode(PersistenceMode.InnerProperty) metadata attribute, which enables page developers to declare the property as the child element of the tag that represents the

ScriptManager on the aspx page

private ServiceReferenceCollection _services;

[PersistenceMode(PersistenceMode.InnerProperty), Editor(“System.Web.UI.Design.CollectionEditorBase,

(continued)

Trang 40

System.Web.Extensions.Design, Version=1.0.61025.0, Culture=neutral,

Listing 15-13 presents the implementation of the replica ServiceReferenceCollection class Thanks

to the NET 2.0 generics, implementing a new type-safe collection class is just a matter of deriving from a

generic collection class such as Collection<ServiceReference>

Listing 15-13: The ServiceReferenceCollection Class

The replica ScriptManager server control also exposes a Boolean property named EnablePageMethods ,

as shown in the following excerpt from Listing 15-12 Page developers can set this property to have the

control generate a client script that defines, instantiates, and initializes the PageMethods client class

public bool EnablePageMethods

Ngày đăng: 12/08/2014, 08:23

TỪ KHÓA LIÊN QUAN