代码改变世界

Asp.net Ajax的使用

2009-08-13 15:20  Andy Ge  阅读(4182)  评论(23编辑  收藏  举报

1.   Asp.net ajax底层异步通信机制:XMLHTTP

对于Asp.net ajax是如何实现底层的异步通信呢?其实关于这一块的技术XMLHttp早就出现了,只不过最近两年才被给予了高度的重视。在Asp.net Ajax的客户端框架中,提供了诸如Sys.Net.WebRequest这样的类型,我们可以通过创建一个该类型的对象,并指定其要调用的服务器端的页面地址,以及对应的参数、超时时间等。但这只不过是对XmlHttp这一技术的包装而已。

1.1. Ajax的技术构成

技术构成如下:

Ø XMLHTTP对象,内置于浏览器中,实现了客户端和服务器端的异步通信。

Ø JSON或者XML,他们定义了客户端和服务器端数据交换的格式。

Ø HTMLCSS,数据表现技术。

Ø JAVASCRIPT,通过JavaScript来操纵浏览器的DOM对象模型,从而实现人机交互。

1.2. 定义一个XmlHttp对象

下面的代码就创建了一个跨浏览器的XmlHttp对象的创建方法。

    function CreateXMLHTTP()

    {          

            var xmlhttp;       

            if(window.XMLHTTPRequest)

            {                  

                    xmlhttp=new XMLHTTPRequest();      

            }

            else if(window.ActiveXObject)

            {                  

                    try

                    {                          

                            xmlhttp=new ActiveXObject("Msxml2.XMLHTTP");                       

                    }

                    catch(el)

                    {

                            xmlhttp=new ActiveXObject("Msxml.XMLHTTP");                

                    }

            } 

            if(xmlhttp == null)

                throw "创建xmlHttp对象失败";

            else

                return xmlhttp;       

                }

1.3. XmlHttp对象的Open方法

例如通过上面的方法创建了一个xmlHttp对象,可以按照如下的方式调用它的Open方法。

xmlHttp.Open(http-method, url, async, userID, password)

Open方法中包含了5个参数,前三个是必要的,后两个是可选的(在服务器需要进行身份验证时提供)。参数的含义如下所示: 

Ø http-method HTTP的通信方式,比如GET或是 POST

Ø url 接收XML数据的服务器的URL地址。通常在URL中要指明 ASPCGI程序

Ø async 一个布尔标识,说明请求是否为异步的。如果是异步通信方式(true),客户机就不等待服务器的响应;如果是同步方式(false),客户机就要等到服务器返回消息后才去执行其他操作

Ø userID 用户ID,用于服务器身份验证

Ø password 用户密码,用于服务器身份验证

1.4. XmlHttp异步处理方式

通过设定xmlHttp对象的onreadystatechange属性,我们可以指定当xmlhttp对象的状态发生更改时候的处理函数,如:

xmlhttp.onreadystatechange =  HandleStateChange;

1.5. XmlHttp对象的Send方法

Open方法对xmlHttp对象进行初始化后,调用Send方法发送数据:

xmlhttp.Send(data)

Send方法的参数类型是Variant,可以是字符串、DOM树或任意数据流。发送数据的方式分为同步和异步两种。在异步方式下,数据包一旦发送完毕,就结束Send进程,客户机执行其他的操作;而在同步方式下,客户机要等到服务器返回确认消息后才结束Send进程。

1.6. xmlHttp对象的异步处理函数

1.6.1.   readyState

XMLHTTP对象中的readyState属性能够反映出服务器在处理请求时的进展状况。客户机的程序可以根据这个状态信息设置相应的事件处理方法。属性值及其含义如下表所示:

0 (未初始化)

对象已建立,但是尚未初始化(尚未调用open方法)

1 (初始化)

对象已建立,尚未调用send方法

2 (发送数据)

send方法已调用,但是当前的状态及http头未知

3 (数据传送中)

已接收部分数据,因为响应及http头不全,这时通过responseBody和responseText获取部分数据会出现错误,

4 (完成)

数据接收完毕,此时可以通过通过responseBody和responseText获取完整的回应数据

 

1.6.2.   客户机处理响应信息

客户机接收到返回消息后,进行简单的处理,基本上就完成了C/S之间的一个交互周期。客户机接收响应是通过XMLHTTP对象的属性实现的:

Ø responseTxt:将返回消息作为文本字符串;

Ø responseXML:将返回消息视为XML文档,在服务器响应消息中含有XML数据时使用;

Ø responseStream:将返回消息视为Stream对象

1.6.3.   status

通过判断XMLHTTP对象的status,我们可以具体的判断本次请求的实际情况,成功,失败,根据这些状态码我们就可以调用执行成功或者失败的回调函数。长整形标准xmlhttp状态码,定义如下:

Number

Description

100

Continue

101

Switching protocols

200

OK

201

Created

202

Accepted

203

Non-Authoritative Information

204

No Content

205

Reset Content

206

Partial Content

300

Multiple Choices

301

Moved Permanently

302

Found

303

See Other

304

Not Modified

305

Use Proxy

307

Temporary Redirect

400

Bad Request

401

Unauthorized

402

Payment Required

403

Forbidden

404

Not Found

405

Method Not Allowed

406

Not Acceptable

407

Proxy Authentication Required

408

Request Timeout

409

Conflict

410

Gone

411

Length Required

412

Precondition Failed

413

Request Entity Too Large

414

Request-URI Too Long

415

Unsupported Media Type

416

Requested Range Not Suitable

417

Expectation Failed

500

Internal Server Error

501

Not Implemented

502

Bad Gateway

503

Service Unavailable

504

Gateway Timeout

505

HTTP Version Not Supported


如:

function HandleStateChange()

{

 if (xmlhttp.readyState == 4) //客户端已经完全加载完毕

 {

                   if(xmlhttp.status == 200)

{

                             alert("Result = " + xmlhttp.responseXML.xml);

        //成功执行,调用成功执行的回调函数

}

else

{

       

}

          //…

 }

 else

                   ….

}


2.   Asp.net Ajax系统框架

Asp.net Ajax提供了完整的客户端和服务器端的模型框架。客户端与服务器端的通信模型。


2.1. 客户端框架

只要当前的页面中,包含引用ScriptManager控件,系统就会将“MicrosoftAjax.js”加载到客户端,完成整个客户端架构的构建。关于客户端的架构结构请参考下图:


具体的类库截图如下:


2.2. 服务器端框架

服务器端的模型分层如下:


Asp.net Ajax框架提供的服务器端的类库如下:


 

3.   针对JavaScript基本类型的扩展

JavaScript是一门非常强大的基于对象(Object Based)的语言,但是对面向对象(Object Oriented)的支持还存在一些不足,同时JavaScript内建的类库也比较简单,甚至缺乏一些很常用的功能。ASP.NET ajax在运行时扩展了JavaScript,大大增强了它的面向对象支持能力,并扩展了一些开发时常用的操作。

3.1. String对象的扩展

Name

Description

endsWith Function

Determines whether the end of the String object matches the specified string.

format Function

Replaces each format item in a String object with the text equivalent of a corresponding object's value.

localeFormat Function

Replaces the format items in a String object with the text equivalent of a corresponding object's value. The current culture is used to format dates and numbers.

startsWith Function

Determines whether the start of the String object matches the specified string.

trim Function

Removes leading and trailing white space from a String object instance.

trimEnd Function

Removes trailing white space from a String object instance.

trimStart Function

Removes leading white space from a String object instance.

 

3.2. Array对象的扩展

Name

Description

add Function

Adds an element to the end of an Array object.

addRange Function

Copies all the elements of the specified array to the end of an Array object.

clear Function

Removes all elements from an Array object.

clone Function

Creates a shallow copy of an Array object.

contains Function

Determines whether an element is in an Array object.

dequeue Function

Removes the first element from an Array object.

enqueue Function

Adds an element to the end of an Array object.

note

Use the add function instead of the Array.enqueue function.

forEach Function

Performs a specified action on each element of an Array object.

indexOf Function

Searches for the specified element of an Array object and returns its index.

insert Function

Inserts a value at the specified location in an Array object.

parse Function

Creates an Array object from a string representation.

remove Function

Removes the first occurrence of an element in an Array object.

removeAt Function

Removes an element at the specified location in an Array object.

 

3.3. Date对象的扩展

Name

Description

format Function

Formats a date by using the invariant (culture-independent) culture.

localeFormat Function

Creates a date from a locale-specific string using the current culture.

parseLocale Function

Creates a date from a locale-specific string using the current culture.

parseInvariant Function

Creates a date from a string using the invariant culture.

 

3.4. Number对象的扩展

 

Name

Description

format Function

Formats a number by using the invariant culture.

localeFormat Function

Formats a number by using the current culture.

parseInvariant Function

Returns a numeric value from a string representation of a number.

parseLocale Function

Creates a number from a locale-specific string.

 

3.5. Error

Name

Description

argument Function

Creates an Error object that represents the Sys.ArgumentException exception.

argumentNull Function

Creates an Error object that represents the Sys.ArgumentNullException exception.

argumentOutOfRange Function

Creates an Error object that represents the Sys.ArgumentOutOfRangeException exception.

argumentType Function

Creates an Error object that represents the Sys.ArgumentTypeException exception.

argumentUndefined Function

Creates an Error object that represents the Sys.ArgumentUndefinedException exception.

create Function

Creates an Error object that has optional additional error information.

invalidOperation Function

Creates an Error object that represents the Sys.InvalidOperationException exception.

notImplemented Function

Creates an Error object that represents the Sys.NotImplementedException exception.

parameterCount Function

Creates an Error object that represents the Sys.ParameterCountException exception.

popStackFrame Function

Updates the fileName and lineNumber properties of an Error instance to indicate where the error was thrown instead of where the error was created. Use this function if you are creating custom error types.

 

3.6. Sys.StringBuilder

3.6.1.   Constructors

StringBuilder Constructor

Creates a new instance of StringBuilder and optionally accepts initial text to concatenate.

3.6.2.   Members

append Method

Appends a string to the end of the StringBuilder instance.

appendLine Method

Appends a new string with a line terminator to the end of the StringBuilder instance.

clear Method

Clears the contents of the StringBuilder instance.

isEmpty Method

Determines whether the StringBuilder instance has any content.

toString Method

Creates a string from the contents of a StringBuilder instance.

3.7. Object

新增的如下两个方法:

Name

Description

getType Function

Returns the type of a specified object instance.

getTypeName Function

Returns a string that identifies the run-time type name of an object.

 

3.8. 使用举例

3.8.1.   String

    <script type="text/javascript">

   

    var a = " abc ";

 

    // 同 C#

    document.write(a.startsWith("a"));

    document.write(" ");

    document.write(a.endsWith("c"));

    document.write("<br />");

   

    a = a.trimStart();

    document.write(a.startsWith("a"));

    document.write("<br />");

 

    a = a.trimEnd();

    document.write(a.endsWith("c"));

    document.write("<br />");

   

    a = " " + a + " ";

    a = a.trim();

    document.write(a.startsWith("a"));

    document.write(" ");

    document.write(a.endsWith("c"));

    document.write("<br />");

   

    var user =

    {

        Name: "webabcd",

        Birthday: new Date(1980, 2, 14)

    };

   

    // String.localeFormat();

    document.write(String.format("Name:{0},Birthday:{1:yyyy-MM-dd}", user.Name, user.Birthday));

    document.write("<br />");

   

   

    // 自定义格式化的实现

    Type.registerNamespace('Demo');

   

    Demo.CustomFormattedString = function()

    {

   

    }

    Demo.CustomFormattedString.prototype =

    {

        // 实现toFormattedString方法,从而实现自定义格式化

        toFormattedString: function(format)

        {

            return "自定义格式化:" + format;

        }

    }

    Demo.CustomFormattedString.registerClass('Demo.CustomFormattedString');

 

    document.write(String.format("{0:测试信息}", new Demo.CustomFormattedString()));  

             

    </script>

输出结果:

false false
true
true
true true
Name
webabcdBirthday1980-03-14
自定义格式化:测试信息

3.8.2.   Array

    <script type="text/javascript">

       

    function btnArray_onclick()

    {

        Sys.Debug.clearTrace();

   

        var ary = ['a', 'b', 'c'];

        var result;

 

        // 向数组末尾处添加一个元素

        Array.add(ary, 'd');

        Sys.Debug.trace("数组ary的元素数:" + ary.length + ",最后一个为:" + ary[ary.length - 1]);

 

        var b = ['e', 'f'];

        // 向数组末尾处添加一个数组

        Array.addRange(ary, b);

        Sys.Debug.trace("数组ary的元素数:" + ary.length + ",最后一个为:" + ary[ary.length - 1]);

       

        // clone一个数组

        var c = Array.clone(ary);

        Sys.Debug.trace("数组c的元素数:" + c.length + ",最后一个为:" + c[c.length - 1]);

       

        // 清除数组内所有元素

        Array.clear(c)

        Sys.Debug.trace("数组c的元素数:" + c.length + ",最后一个为:" + c[c.length - 1]);

       

        // 移除数组首元素,返回值为移除的元素

        result = Array.dequeue(ary);

        Sys.Debug.trace("数组ary的元素数:" + ary.length + ",移除的元素:" + result);

       

        // 向数组末尾处添加一个元素

        Array.enqueue(ary, "g");

        Sys.Debug.trace("数组ary的元素数:" + ary.length + ",最后一个为:" + ary[ary.length - 1]);

       

        // 数组内是否包含某个元素,返回true或false

        result = Array.contains(ary, "c");

        Sys.Debug.trace("数组ary的元素数:" + ary.length + ",其中有“c”吗?" + result);

       

        // 移除数组中的某个元素

        Array.remove(ary, 'g');

        Sys.Debug.trace("数组ary的元素数:" + ary.length + ",最后一个为:" + ary[ary.length - 1]);

  

        // 移除数组中的某个元素

        Array.removeAt(ary, 4);

        Sys.Debug.trace("数组ary的元素数:" + ary.length + ",最后一个为:" + ary[ary.length - 1]);

       

        // 向数组中添加一个元素

        Array.insert(ary, 4, 'f');

        Sys.Debug.trace("数组ary的元素数:" + ary.length + ",最后一个为:" + ary[ary.length - 1]);

 

        // 数组中某个元素的位置,返回值为某元素的位置索引

        result = Array.indexOf(ary, 'd');

        Sys.Debug.trace("数组ary的元素数:" + ary.length + ",搜索其中“d”的位置:" + result);

        result = Array.indexOf(ary, 'd', 3);

        Sys.Debug.trace("数组ary的元素数:" + ary.length + ",从索引“3”处开始,搜索其中“d”的位置:" + result);

   

        var s = "['g', 'h']";

        // 将字符串解析为数组

        Array.addRange(ary, Array.parse(s));

        Sys.Debug.trace("数组ary的元素数:" + ary.length + ",最后一个为:" + ary[ary.length - 1]);

       

        // foreach

        Array.forEach(ary, appendToString, "|");

    }

   

    function appendToString(arrayElement, index, array)

    {

        // “this”在这里代表上下文参数,即“|”

        Sys.Debug.trace(arrayElement + this + index);

    }

   

    </script>

输出结果:

数组ary的元素数:4,最后一个为:d

数组ary的元素数:6,最后一个为:f

数组c的元素数:6,最后一个为:f

数组c的元素数:0,最后一个为:undefined

数组ary的元素数:5,移除的元素:a

数组ary的元素数:6,最后一个为:g

数组ary的元素数:6,其中有“c”吗?true

数组ary的元素数:5,最后一个为:f

数组ary的元素数:4,最后一个为:e

数组ary的元素数:5,最后一个为:f

数组ary的元素数:5,搜索其中“d”的位置:2

数组ary的元素数:5,从索引“3”处开始,搜索其中“d”的位置:-1

数组ary的元素数:7,最后一个为:h

b|0

c|1

d|2

e|3

f|4

g|5

h|6

 

Arrayforeach使用

语法:Array.forEach(array, method, context);

参数说明:

Term

Definition

array

The Array object to enumerate.

method

The function to call for each element in the array.

instance

The context for calling method.

举例:

var a = ['a', 'b', 'c', 'd'];

a[5] = 'e';

var result = '';

 

function appendToString(element, index, array) {

    // "this" is the context parameter, i.e. '|'.

    result += element + this + index + ',';

}

Array.forEach(a, appendToString, '|');

// View the results: a|0,b|1,c|2,d|3,e|5,

alert(result);

 

 

 

 

3.8.3.   Date

    <script type="text/javascript">

   

    var d = new Date();

   

    // d.localeFormat();

    // 格式化Date

    document.write(d.format("yyyy年MM月dd日HH时mm分ss秒,星期dddd"));

   

    document.write("<br />"); 

   

    // Date.parseLocale();

    // 将字符串解析为Date

    document.write(Date.parseInvariant("1980-02-14", "yyyy-MM-dd"));

   

    </script>

输出结果:

20080123133929秒,星期Wednesday
Thu Feb 14 00:00:00 UTC+0800 1980

3.8.4.   Number

    <script type="text/javascript">

 

    var a = 999.999;

   

    // a.localeFormat();

    // 格式化数字

    document.write(a.format("p"));

    document.write("<br />");

    document.write(a.format("d"));

    document.write("<br />");

    document.write(a.format("c"));

    document.write("<br />");

    document.write(a.format("n"));

    document.write("<br />");

 

    var x = "100";

    var y = "200";

    // Number.parseLocale();

    // 解析字符串为数字

    document.write(Number.parseInvariant(x) + Number.parseInvariant(y));

 

    </script>

输出结果:

999.99 %

999.999

¤999.99

999.99

300

3.8.5.   StringBuilder

    <script type="text/javascript">

        function buildString(title)

        {

            // 创建一个StringBuilder对象

            var sb = new Sys.StringBuilder("aaa");

            // toString() - 将StringBuilder对象的内容转换为字符串

            Sys.Debug.trace("StringBuilder:" + sb.toString());

           

            // 添加指定字符串到StringBuilder对象的结尾

            sb.append("bbb");

            Sys.Debug.trace("StringBuilder:" + sb);

 

            // 添加指定字符串到StringBuilder对象的结尾并添加一个换行符

            sb.appendLine("ccc");

            Sys.Debug.trace("StringBuilder:" + sb);

           

            // 添加一个换行符到StringBuilder对象的结尾

            sb.appendLine();

            // toString(separator) - 在StringBuilder对象内的每一个元素的结尾处添加指定字符串

            // 然后将StringBuilder对象的内容转换为字符串

            Sys.Debug.trace("StringBuilder:" + sb.toString('xxx'));

 

            // 清除StringBuilder对象所有内容

            sb.clear();

            Sys.Debug.trace("StringBuilder:" + sb);

           

            // StringBuilder对象的内容是否为空

            var bln = sb.isEmpty();

            Sys.Debug.trace("StringBuilder:" + bln);

        }

 

        function pageLoad()

        {

            buildString();

        }

    </script>

输出结果:

StringBuilderaaa

StringBuilderaaabbb

StringBuilderaaabbbccc

 

StringBuilderaaaxxxbbbxxxccc

xxx

 

StringBuilder

StringBuildertrue 

 

3.8.6.   Object

    Type.registerNamespace("Demo");

    Demo.Message = function(content, publishTime) {

        this._content = content;

        this._publishTime = publishTime;

    }

    Demo.Message.registerClass('Demo.Message');

 

    var d = new Date();

    var testMessage = new Demo.Message('hello', d);

   

    // 顾名思义:getTypeName

    document.write(Object.getTypeName(testMessage));

   

    document.write("<br />");

   

    // 顾名思义:getType

    document.write(Object.getType(testMessage));

执行结果如下:

Demo.Message

function(content, publishTime) { this._content = content; this._publishTime = publishTime; }

3.9. 新增类型Type

3.9.1.   Type---提供面向对象最基本的支持

下表中给中的方法有的类似于静态方法,通过Type直接调用,如RegisterNamespace,有的是通过具体的类型来调用,如:RegiesterClass等。

Name

Description

callBaseMethod Method

Invokes a base class method with specified arguments.

getBaseMethod Method

Returns the implementation of a method from the base class of the specified instance.

getBaseType Method

Returns the base type of an instance.

getInterfaces Method

Returns an Array object that contains the list of interfaces that the type implements.

getName Method

Returns the name of the instance type.

getRootNamespaces Method

Returns an Array object that contains references to all the root namespaces of the client application.

implementsInterface Method

Determines whether the type implements a specified interface.

inheritsFrom Method

Determines whether the type inherits from the specified parent type.

initializeBase Method

Initializes the base class and its members in the context of a given instance, providing the model for inheritance and for initializing base members.

isClass Method

Returns a value that indicates whether the specified type is a class.

isImplementedBy Method

Determines whether an instance implements the specified interface.

isInstanceOfType Method

Determines whether an object is an instance of either a specified type or of one of its derived types.

isInterface Method

Returns a value that indicates whether the specified type is an interface.

isNamespace Method

Returns a value that indicates whether the specified object is a namespace.

registerEnum Method

Registers a class as an enumeration.

parse Method

Returns an instance of the type that is specified by a type name.

registerClass Method

Registers a class that is defined by the constructor, with an optional base type and with interfaces.

registerInterface Method

Registers an interface that is specified by its constructor.

registerNamespace Method

Registers and creates a namespace.

resolveInheritance Method

Copies members from the base class to the prototype that is associated with the derived class, and continues this process up the inheritance chain. This enables you to reflect on the inherited members of a derived type

 

3.9.2.   继承

<script type="text/javascript">

    // Register classes to test.

    Type.registerNamespace('Samples');

 

    Samples.A = function(){}

 

Samples.B = function()   

{

        // Initialize as a base class.

        Samples.A.initializeBase(this);

    }

 

    Samples.C = function(){}

    Samples.ID = function(){}

    Samples.D = function(){}

 

    Samples.A.registerClass('Samples.A');

    Samples.B.registerClass('Samples.B', Samples.A);

 

    Samples.C.registerClass('Samples.C');

 

    Samples.ID.registerInterface('Samples.ID');

    Samples.D.registerClass('Samples.D',Samples.B,Samples.ID);

 

    var isDerived;

    isDerived = Samples.B.inheritsFrom(Samples.A);

    // Output: "true".

    alert(isDerived);

 

    isDerived = Samples.C.inheritsFrom(Samples.A);

    // Output: "false".

    alert(isDerived);

 

    isDerived = Samples.D.inheritsFrom(Samples.B);

    //Output: "true";

    alert(isDerived);

 

    isDerived = Samples.D.implementsInterface(Samples.ID);

    //Output: "true";

    alert(isDerived);

 

</script>

3.9.3.   名称空间

Type.registerNamespace("Web.Utility");
// Implement Web Utility
Type.registerNamespace("Web.Performance");
// Implement Performance

 

 

3.9.4.   定义Enum类型

       <script type="text/javascript">

            // Register classes to test.

            Type.registerNamespace('Samples');

 

            // Define an enumeration type and register it.

            Samples.LineType = function(){};

            Samples.LineType.prototype =

            {

                Solid:      0,

                Wide:       1,

                Rounded:    2

            }

            Samples.LineType.registerEnum("Samples.LineType");

 

            // Define an enumeration type and register it.

            Samples.Color = function(){};

            Samples.Color.prototype =

            {

                Red:    0xFF0000,

                Blue:   0x0000FF,

                Green: 0x00FF00,

                White: 0xFFFFFF

            }

            Samples.Color.registerEnum("Samples.Color");

 

            // Define an enumeration flags type and register it.

            Samples.Permissions = function(){};

            Samples.Permissions.prototype =

            {

                Create: 0x0,

                Read:   0x2,

                Update: 0x4,

                Delete: 0x8

            }

            Samples.Permissions.registerEnum("Samples.Permissions", true);

 

 

            // Output: "1".

            alert(Samples.LineType.Wide);

 

            // Output: "16711680".

            alert(Samples.Color.Red);

 

            // Output: "4".

            alert(Samples.Permissions.Update);

 

        </script>

4.   JSON

4.1. 定义

JSONJavascript Object Notation的简写。在过去客户端和服务器端之间传送数据,常采用xml,但由于Javascriptxml的操作有一定的复杂性,近年来,又引入了一种新的在客户端和服务器端传送数据的方式,即Json。这种方式起源于Javascript对对象的定义方式引起。如下面的对象定义:

var person ={name:'bill',age:24,address:'beijing',weight:120,height:180};

和下面的代码都可以创建一个Person对象。

    var str = "var person ={name:'bill',age:24,address:'beijing',weight:120,height:180}";

    eval(str);

alert(person.address);

运行上面的代码,能够根据str字符串构建出Person对象,并显示出当前Person对象的地址。

4.2. 如何在服务器端序列化和反序列化Json

在服务器端使用JavaScriptSerializer对象的SerializeDeserialize方法来完成。

如:

    protected void Button2_Click(object sender, EventArgs e)

    {

        Person person = new Person();

        person.Name = "Bill";

        person.Age = 49;

        person.Weight = 220;

        JavaScriptSerializer serializer = new JavaScriptSerializer();

        this.lblResult.Text = serializer.Serialize(person);

    }

    protected void Button1_Click(object sender, EventArgs e)

    {

        JavaScriptSerializer serializer = new JavaScriptSerializer();

        Person person = serializer.Deserialize<Person>(this.lblResult.Text);

        lblResult.Text = string.Format("当前用户的信息如下:Name:{0};Age:{1};Weight:{2}", person.Name, person.Age, person.Weight);

    }

4.3. 如何在客户端序列化和反序列化Json

在客户端需要用到Sys.Serialization.JavaScriptSerializer.serializedeserialize方法。

序列化如下:

var person = new MyNameSpace.Person();

person.Name = "Bill";

person.Age = 55;

person.Address = "US";

var jsonString = Sys.Serialization.JavaScriptSerializer.serialize(person);

jsonString的值为:{"__type":"MyNameSpace.Person","Name":"Bill","Age":55,"Address":"US"}

反序列化如下:

var person = Sys.Serialization.JavaScriptSerializer.deserialize(jsonString);

var objectStr = String.format("当前用户信息:Name:{0};Age:{1};Address:{2}",person.Name,person.Age,person.Address);

objectStr的值为:

当前用户信息:Name:Bill;Age:55;Address:US

 

4.4. 客户端和服务器端的对象传递

利用asp.net ajax的框架我们能够实现对象在服务器端和客户端的来回传递,而不需要做额外的工作,即可以直接在客户端创建一个Javascript的对象,调用服务器端的方法时,直接传递过去,从服务器端返回的对象在客户端也可以直接的使用。

5.   自定义JsonSerializationConverter

如果服务器端的对象类型涉及到很多其他类型的引用或者会导致循环引用的问题,并且这些类型很难序列化到客户端这时候就需要针对当前的类型自定义一个JsonSerializationConverter转换器。

5.1. 该类型派生于JavaScriptConveter

该类型必须是JavaScriptConverter的子类型,才能够在被序列化到客户端的时候被ajax的框架所调用。

    public class NodeConverter : JavaScriptConverter

5.2. 重载基类的Serialize方法

定义一个Dictionary<string,object>类型的字典,将该类型要序列化的成员及其值一次加入其中,并将该字典作为结果返回。

        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)

        {

            Dictionary<string, object> dict = new Dictionary<string, object>();

 

            Node node = (Node)obj;

            dict.Add("text", node.Text);

            dict.Add("value", node.Value);

            dict.Add("toolTip", node.ToolTip);

            dict.Add("nodeOpenImg", node.NodeOpenImg);

            dict.Add("nodeCloseImg", node.NodeCloseImg);

            dict.Add("extendedData", node.ExtendedData);

            dict.Add("expanded", node.Expanded);

            dict.Add("showCheckBox", node.ShowCheckBox);

            dict.Add("checked", node.Checked);

            dict.Add("isLeaf", node.IsLeaf);

            string className = string.Empty;

            if (node.StyleWrapper == null)

            {

                if (node.ClassName == string.Empty)

                    className = "";

                else

                    className = node.ClassName;

            }

            else

                className = node.StyleWrapper.Name;

 

            dict.Add("className", className);

 

            Node[] nodes = new Node[node.Nodes.Count];

            for (int i = 0; i < nodes.Length; i++)

            {

                nodes[i] = node.Nodes[i];

            }

            dict.Add("nodes", nodes);

 

            dict.Add("subNodesLoaded", node.SubNodesLoaded);

            return dict;

        }

 

5.3. 重载基类的Deserialize方法

首先创建一个该类型的对象实例,根据传入的Dictionary<string,object>,依次读取在上述方法中所保存的值,并赋值到对象对应的成员上,然后将该对象返回。

如:

        public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)

        {

            Node node = new Node();

 

            node.Text = (string)dictionary["text"];

            node.Value = (string)dictionary["value"];

            node.ToolTip = (string)dictionary["toolTip"];

            node.NodeOpenImg = (string)dictionary["nodeOpenImg"];

            node.NodeCloseImg = (string)dictionary["nodeCloseImg"];

            node.ExtendedData = (string)dictionary["extendedData"];

            node.Checked = (bool)dictionary["checked"];

            node.Expanded = (bool)dictionary["expanded"];

            node.SubNodesLoaded = (bool)dictionary["subNodesLoaded"];

            node.ShowCheckBox = (bool)dictionary["showCheckBox"];

            node.ClassName = (string)dictionary["className"];

            node.IsLeaf = (bool)dictionary["isLeaf"];

            ArrayList nodes = (ArrayList)dictionary["nodes"];

            for (int i = 0; i < nodes.Count; i++)

            {

                node.Nodes.Add((Node)Deserialize((IDictionary<string, object>)nodes[i], type, serializer));

            }

 

            return node;

        }

5.4. 重载SurportedTypes方法

返回有要转换的类型所构成的数组。

针对实体类型的JsonSerializationConverter具体使用,请参考MyControlDemo2中的树结点的使用。

        public override IEnumerable<Type> SupportedTypes

        {

            get

            {

                return new System.Type[] { typeof(Node) };

            }

        }

 

5.5. 修改配置文件启用自定义转换器

在对应的converters节下添加自定义的转化器类型定义。如:

        <system.web.extensions>

                <scripting>

                        <webServices>

                                <jsonSerialization>

                                        <converters>

                                                <add name="ConvertMe" type="ChinaCustoms.Application.HC2006.Web.CustomControls.NodeConverter"/>

                                        </converters>

                                </jsonSerialization>

                        </webServices>

                </scripting>

        </system.web.extensions>

6.   如何将DataSet对象传输到客户端

6.1. 添加Microsoft.Web.Preview.dll程序集引用

该程序集在安装完Microsoft.Web.Preview SerializerASP.NET AJAX January Futures CTP 中包括的一个程序集, 含有DatSet/DataTable/DataRow这些循环引用对象的序列化类, 使用时只要将Microsoft.Web.Preview.dll添加到bin目录下面。

6.2. 在配置文件中添加如下Converters

        <jsonSerialization>

          <converters>

            <add name="DataSetConverter" type="Microsoft.Web.Preview.Script.Serialization.Converters.DataSetConverter, Microsoft.Web.Preview" />

            <add name="DataRowConverter" type="Microsoft.Web.Preview.Script.Serialization.Converters.DataRowConverter, Microsoft.Web.Preview" />

            <add name="DataTableConverter" type="Microsoft.Web.Preview.Script.Serialization.Converters.DataTableConverter, Microsoft.Web.Preview" />           

          </converters>

        </jsonSerialization>

6.3. 客户端类型结构可参照服务器端

6.3.1.   简述

如果返回的是DataSet对象,则可以通过DataSet.tables得到其表格的集合,从而可以访问到每一个数据表。

通过Table.rows可以访问到当前数据表的行集合,因而可以访问到每一行。

通过Table.columns可以访问到当前数据的所包含的列集合,可以得知其每一列的名称、类型等。

通过Table.rows[rowIndex][columnName]可以访问每一行每个单元格中的具体数值。

6.3.2.   客户端的DataSet的结构说明

服务器端返回DataSetJson串如下:

            {"tables":[{"columns":[{"name":"ID","dataType":"Number","defaultValue":null,"readOnly":false,"isKey":false},{"name":"TITLE","dataType":"String","defaultValue":null,"readOnly":false,"isKey":false},{"name":"TIME","dataType":"Date","defaultValue":null,"readOnly":false,"isKey":false},{"name":"DOCTYPE","dataType":"Number","defaultValue":null,"readOnly":false,"isKey":false}],"rows":[{"ID":20,"TITLE":"测试2","TIME":""/Date(1213113600000)"/","DOCTYPE":2}]},{"columns":[{"name":"ID","dataType":"Number","defaultValue":null,"readOnly":false,"isKey":false},{"name":"TITLE","dataType":"String","defaultValue":null,"readOnly":false,"isKey":false},{"name":"TIME","dataType":"Date","defaultValue":null,"readOnly":false,"isKey":false},{"name":"DOCTYPE","dataType":"Number","defaultValue":null,"readOnly":false,"isKey":false}],"rows":[{"ID":33,"TITLE":"海关系统信访举报情况","TIME":""/Date(1213113600000)"/","DOCTYPE":2},{"ID":25,"TITLE":"衔级晋升培训率","TIME":""/Date(1213113600000)"/","DOCTYPE":2},{"ID":20,"TITLE":"测试2","TIME":""/Date(1213113600000)"/","DOCTYPE":2},{"ID":16,"TITLE":"衔级晋升培训率","TIME":""/Date(1213113600000)"/","DOCTYPE":2}]}],"__type":"System.Data.DataSet"}

 

            这是一个Ajax调用服务器端的方法,返回一个Dataset到客户端,而这个返回到客户端DataSet的结构从上面的数据可以分析如下:

            这个结果包含有两个dataTable

           

            DataSet包含有两个属性:

            第一个是:tables,对于tables的内容下面有分析。

            第二个是:__type,其对应的值为:System.Data.DataSet

 

            tables集合的分析:

            1、通过012...等索引可以访问每一个table对象;

            2、对于每一个table对象由定义了如下的属性:

                        Acolumns:列集合,通过012...可以访问每一个列对象;

                                    对于每一个列对象又包含如下属性定义:

                                    aname:列名称

                                    bdataType:数据类型

                                    cdefaultValue:默认值

                                    dreadOnly:是否只读

                                    eisKey:是否是主键

                        Brows:行集合,通过012等可以访问每一个行对象

                                    对于每一个行对象,需要通过如下方式来访问该行某一列的数据:

                                    var curRow = returnDataSet.tables[0].rows[0];

                                    var id = curRow["ID"];

                                    或者通过:

                                    var id = curRow.ID;来访问。

                                    而不能够通过0,1,2等索引来访问了,其他的列访问相同。

7.   如何在页面的脚本中调用服务器端方法

7.1. 方法的声明

能够在客户段页面进行调用的服务器端的方法必须为静态方法,并且该方法要标识有[WebMethod]特性。如:

    [WebMethod]

    public static DateTime GetServerSideTime()

    {       

        return DateTime.Now;

}

7.2. 修改<ScriptManager>EnablePageMethods="true"

            <asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true">

            </asp:ScriptManager>

7.3. 客户端的方法调用通过PageMethods来调用

PageMethods.GetServerSideTime(onSucceed,onFail);

目前该方法对List<T>类型的数据存在缓存的问题,只有当站点重新启动以后,才能显示出新的变化。

8.   如何将服务器端类型定义到客户端JavaScript类型

8.1. 在当前的页面类或者要引用的WebService类前添加[GenerateScriptType]标识

Asp.net ajax会默认的在暴露到客户端的方法中用到的类型给出对应的Javascript类型。如果我们需要把其他的类型的定义在客户端用Javascript给出,我们可以采用[GenerateScriptType]特性标识。

[ScriptService]

[GenerateScriptType(typeof(TestNamespace.MyDto))]

public class WebService : System.Web.Services.WebService

{

}

注意:如果在aspx的前端页面中使用服务器端的类型,需要该页面中的ScriptManager启用EnnablePageMethod方法,同时在后台的代码中添加[GenerateScriptType]特性标识,并且该页面中还必须包含有[WebMethod]标识的静态方法。

8.2. 客户端如何使用该类型

上面的WebService中要用到MyDto类型,通过这个标识可以生成客户端对应的MyDto类型,在页面的脚本中就可以直接按照如下方式创建MyDto对象,如果服务器端的方法需要的是MyDto类型的参数,就可以直接把该参数对象传递过去。

var person = new TestNamespace.MyDto();

9.   如何将Web服务暴露到客户端

9.1. WebService类上添加[ScriptService]标识

[ScriptService]

public class WebService : System.Web.Services.WebService

{

……

}

9.2. 在要暴露到页面的方法上添加[WebMethod]标识

    [WebMethod]

    public Person GetBillInfo()

    {

        Person bill = new Person("Bill", 58, 130, 178);

        return bill;

    }

9.3. 服务器端重载方法的实现

如果服务器方法有重载时,在客户端注册的代理只会有一个(因为js本身没有overload功能)

可以在服务器使用[WebMethod(MessageName="XXXX")]为重载的方法注册不同的代理名称

[WebMethod]

public int GetRandom()

{

   return new Random(DateTime.Now.Millisecond).Next();

}

 

[WebMethod(MessageName="GetRangeRandom")]

public int GetRandom(int minValue, int maxValue)

{

   return new Random(DateTime.Now.Millisecond).Next(minValue, maxValue);

}

 

9.4. 更改客户端调用的方式

利用[ScriptMethod(UseHttpGet=true)]可以更改当前方法的调用方式为Get方式,如:

    [WebMethod(MessageName = "GetRangeRandom")]

    [ScriptMethod(UseHttpGet = true)]

    public int GetRandom(int minValue, int maxValue)

    {

        return new Random(DateTime.Now.Millisecond).Next(minValue, maxValue);

    }

9.5. 在具体的页面中添加ServiceReference,实现对当前WebService的引用

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

                <Services>

                    <asp:ServiceReference Path="~/WebService.asmx" />

                </Services>

            </asp:ScriptManager>

 

9.6. 如何在客户端调用

可以有两种方式来调用:

Ø [namespace].className.methodName(….) 访问WebServices方法

Ø 客户端代理对象实例调用,如:
var proxy = new [namespace].className();
proxy.methodName(….);

10.        客户端调用服务器端方法的参数签名和回调方法的签名

10.1. 调用Web Service

var myServiceProxy = MyNameSpace.MyService();

myServiceProxy.MyServiceMethod(param1, param2, ..., succeededCallback, failedCallback, userContext);

Ø param - 在前面顺序地写参数

Ø succeededCallback - 调用成功的回调函数

Ø failedCallback - 调用失败的回调函数

Ø userContext - 用户上下文

10.2. 调用成功的回调函数

function succeededCallback(result, userContext, methodName) { }

Ø result - 调用WebService的方法后返回的数据

Ø userContext - 用户上下文

Ø methodName - 调用的方法名

10.3. 调用失败的回调函数

function failedCallback(error, userContext, methodName) { }

Ø error - 调用WebService的方法后返回的数据(Sys.Net.WebServiceError对象),其中包含有当前错误的详细信息属性。

Ø userContext - 用户上下文

Ø methodName - 调用的方法名

11.        如何在客户端调用服务器端的方法中使用Session

11.1. 默认情况下,Ajax框架在异步调用的方法中禁用了Session

如果使用了Session,将会报如下错误:


11.2. 修改[WebMethod]成[WebMethod(EnableSession = true)]来启用Session

    [WebMethod(EnableSession = true)]

    public void AddPerson(Person person)

    {

        List<Person> persons = new List<Person>();

        if (Session["Persons"] != null)

            persons = (List<Person>)Session["Persons"];

        else

            Session["Persons"] = persons;

        persons.Add(person);

    }

12.        ScriptManagerProxy

如果页面采用了母板页,在母板页中已经添加了<ScriptManager>,如果在内容需要引用脚本或者WebService,由于一个页面中只能有一个<ScriptManager>,这时候就必须采用<ScriptManagerProxy>来完成,其使用方式和<ScriptManager>类似。

13.        Asp.net Ajax几种实现页面无刷新方式的比较

13.1. UpdatePanel

缺点:UpdatePanel是这几种实现方式中性能最差、速度最慢的。每一次与服务器的交互,UpdatePanel的实现机制都会将整个页面的所有“表单数据”传回到服务器,而在服务器端也将重新构建页面的所有控件内容,经历完整整个页面生命周期,然后再返回到客户端,客户端再更新UpdatePanel内区域中新的内容,所以对于每一次与服务器的交互,跟原来整个页面回送模型占用的资源是一样,只不过是在客户端做了一些处理,避免了页面的刷新。

UpdatePanel受欢迎的原因是使用起来非常的简单,用户几乎不需要什么学习成本,仍按照原来的编程方式就可以很轻松的实现了页面的无刷新效果。

13.2. Aspx页面中静态方法

由于上面的UpdatePanel存在着明显效能问题,Asp.net Ajax又提供了另一种方式,那就是页面静态方法回调。通过在ScriptManager指定EnablePageMethodTrue,并在当前页面的后台代码中声明标识有[WebMethod]的静态方法,就可以在客户端的脚本中通过PageMethods.静态方法名称来调用后台的方法了。这种方式要求该方法必须为静态方法,调用过程中并不会将整个页面回送到服务器,仅仅来回传送参数或者结果,并且服务器端也不会重新构建一个新的页面实例,所以性能上有很大的提高。

注意在使用这种方式的时候,所有跟页面实例相关的东西都不能在使用,如SessionCaching等,而必须通过HttpContext.Current.Session来访问。

13.3. WebService方式

这是一种最好的方式了,WebService的方法不再是静态方法,在客户端使用的时候,需要添加引用该WebService地址。每一次调用都会通过客户端的代理类来完成调用,在服务器端会按照正常的WebService的执行过程来进行,会构建服务器端的WebService对象,但是构建对象所需要的开销远比页面要小很多。

13.4. Sys.Net.WebRequest

这其实是一种最原始的方法了,利用AjaxxmlHttp包装,我们可以通过WebRequest来直接访问服务器端的aspxashx等。在回调函数中由自己来处理返回到客户端的数据的解析和提取。

14.        如何通过ajax客户端的框架实现Javascript的OOP

14.1. 名称空间声明

// 注册一个名为“Demo”的名称空间

Type.registerNamespace("Demo");

14.2. 接口的声明和注册

// 定义在Demo命名空间下的IContent接口(接口不能有构造函数)

Demo.IContent = function()

{

 

}

 

// 定义Demo命名空间下的IContent接口的方法

Demo.IContent.prototype =

{

    showContent : function()

    {

   

    }

}

 

// 在Demo命名空间下注册IContent接口

Demo.IContent.registerInterface('Demo.IContent');

14.3. 基类的定义和注册

// 定义Demo命名空间下的Message类的构造函数

Demo.Message = function(content, publishTime)

{

    this._content = content;

    this._publishTime = publishTime;

}

 

// 定义Demo命名空间下的Message类的方法

Demo.Message.prototype =

{   

    get_content: function()

    {

        return this._content;

    },

   

    get_publishTime: function()

    {

        return this._publishTime.format("yyyy-MM-dd HH:mm:ss");

    },

   

    toString: function()

    {

        return this.get_content() + " " + this.get_publishTime();

    }

 

}

 

// 在Demo命名空间下注册Message类

Demo.Message.registerClass('Demo.Message');

14.4. 如何实现从基类和接口派生子类

// 定义Demo命名空间下的MessageWithUserId类的构造函数

// 我们之后要让这个类继承自Demo.Message

// 在构造函数内用initializeBase调用基类的构造函数

Demo.MessageWithUserId = function(userId, content, publishTime)

{

    Demo.MessageWithUserId.initializeBase(this, [content, publishTime]);

   

    this._userId = userId;

}

 

// 定义Demo命名空间下的MessageWithUserId类的方法

Demo.MessageWithUserId.prototype =

{

    get_userId: function()

    {

        return this._userId;

    },

   

    showContent: function()

    {

        return Demo.MessageWithUserId.callBaseMethod(this, 'get_content')

    },

   

    // callBaseMethod用于调用基类的方法

    toString: function()

    {

        return this.get_userId() + " " + Demo.MessageWithUserId.callBaseMethod(this, 'toString');

    }

}

 

// 在Demo命名空间下注册MessageWithUserId类

// 他继承自Demo.Message类和Demo.IContent接口

Demo.MessageWithUserId.registerClass('Demo.MessageWithUserId', Demo.Message, Demo.IContent);

 

15.        本地化和资源文件

15.1. 更改浏览器的默认语言环境

切换语言[中文<=>英文]看效果: 客户端设置浏览器的步骤: "工具"-"Internet选项"-"语言"; 如果没有 zh-cnen-US, 将这两项加入, 加放后移到最上面的为默认启用.

15.2. 针对每种语言定义一份Js的资源文件

如:

默认:FruitsResource.js

/// <reference name="MicrosoftAjax.js"/>

Type.registerNamespace("Fruits");

 

Fruits.Fruit =

{

    "Pear" : "梨"

}

美国英语:FruitsResource.en-us.js

/// <reference name="MicrosoftAjax.js"/>

Type.registerNamespace("Fruits");

 

Fruits.Fruit =

{

    "Pear" : "Pear"

}

香港特别行政区:FruitsResource.zh-hk.js

/// <reference name="MicrosoftAjax.js"/>

Type.registerNamespace("Fruits");

 

Fruits.Fruit =

{

    "Pear" : "梨子"

}

注意文件的最后面的扩展名部分,是每种语言的统一约定的简写,不是随便指定的。

15.3. 引用资源文件到页面中

通过ScriptManager来将资源文件引入到页面中。

如:

            <asp:ScriptManager runat="server" ID="sm" EnableScriptLocalization="true">

                <Scripts>

                    <asp:ScriptReference Path="~/Resources/FruitsResource.js"

                        ResourceUICultures="en-us,zh-hk" />

                </Scripts>

            </asp:ScriptManager>

注意要启用EnableScriptLocalization属性,这里还要指定一个很重要的属性ResourceUICultures, 指定支持的客户端语言种类集合, 用逗号分开。

15.4. 修改UICultrue和Culture为auto

可以在配置文件中修改,或者针对某个具体的页面进行。

15.5. 如何使用

按照在资源文件中的定义,直接引用即可,ajax的客户端框架会按照当前的语言环境设置,从对应的资源文件中加载。

alert(Fruits.Fruit.Pear);

16.        身份认证和用户个性化服务

16.1. 身份认证和个性化服务的启用

修改配置文件的对应的配置节即可以实现身份认证和用户个性化服务。修改如下:

        <system.web.extensions>

                <scripting>

                        <webServices>

                                <authenticationService enabled="true" requireSSL = "false"/>

                                <profileService enabled="true"                                                          readAccessProperties="Age,Salary,Article.Title,Article.PublishTime"                                                             writeAccessProperties="Age,Salary,,Article.Title,Article.PublishTime" />

                        </webServices>

                </scripting>

        </system.web.extensions>

如果需要提供自定义的身份认证和用户个性化服务处理,需要在页面的ScriptManager中添加对应的项。如:

        <asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true" OnAsyncPostBackError="ScriptManager1_AsyncPostBackError"

            EnableScriptGlobalization="True">

            <AuthenticationService Path="~/ClientScripting/SysServices/AuthenticationService.asmx" />

            <ProfileService Path="~/ClientScripting/SysServices/ProfileService.asmx" />

        </asp:ScriptManager>

对于这两个WebService的编写,请继续往下看。

16.2. Sys.Services.AuthenticationService

若想自定义身份认证服务,我们需要手工编写一个Web Service,且其中一定要包含Login()Logout()两个方法,分别用来实现用户登录和注销。且这两个方法的签名(包括方法名、参数个数、参数顺序、参数类型以及返回值类型)均要满足一定的要求。

下列代码就是一个自定义身份认证Web Service的原型。我们可以参考该原型中给出的各个方法以及方法的签名,或是基于该原型实现适合实际项目的身份认证方法:

[ScriptService]

public class AuthenticationService : System.Web.Services.WebService

{

    /// <summary>

    /// 登录

    /// </summary>

    /// <param name="userName">用户名</param>

    /// <param name="password">密码</param>

    /// <param name="createPersistentCookie">是否跨浏览器保存认证信息(持久保留)</param>

    /// <returns></returns>

    [WebMethod]

    public bool Login(string userName, string password, bool createPersistentCookie)

    {

        //Place code here.

 

        System.Web.Security.FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);

        return true;

    }

 

    /// <summary>

    /// 注销

    /// </summary>

    [WebMethod]

    public void Logout()

    {

        System.Web.Security.FormsAuthentication.SignOut();

}

}

完成自定义的身份认证Web Service之后,若想在某个ASP.NET AJAX页面中使用这个自定义的身份认证服务,我们需要在ScriptManager中显式声明:

<asp:ScriptManager ID="sm" runat="server">

    <AuthenticationService

        Path="~/CustomApplicationServices/CustomAuthenticationService.asmx" />

</asp:ScriptManager>

其中粗体部分的<AuthenticationService />标签对应着ASP.NET AJAX服务器端的System.Web.UI.AuthenticationServiceManager类型,负责设置页面中自定义身份认证Web Service的位置。将其Path属性设置为前面编写的CustomAuthenticationService.asmx Web Service之后,该页面中的身份认证处理将均由这个自定义的CustomAuthenticationService.asmx处理。

提示:若是将<AuthenticationService />标签的Path属性设置为空字符串,或是不设置该属性,则ASP.NET AJAX仍会使用默认的身份认证服务。

 

16.3. Sys.Services.ProfileService

与自定义身份认证服务类似的是,若想自定义用户个性化服务,我们也需要严格遵循一定的规则(固定的方法个数与方法签名等)手工编写一个Web Service。这里我们给出了一个自定义用户个性化Web Service的原型,至于其中的具体实现方法,需要根据实际项目的需求进行具体分析:

[ScriptService]

public class ProfileService : System.Web.Services.WebService

{

    /// <summary>

    /// 获取用户的全部Profile

    /// </summary>

    /// <returns></returns>

    [WebMethod]

    public IDictionary<string, object> GetAllPropertiesForCurrentUser()

    {

        Dictionary<string, object> dic = new Dictionary<string, object>();

 

        ProfileCommon profile = (ProfileCommon)HttpContext.Current.Profile;

        dic.Add("Age", profile.Age);

        dic.Add("Salary", profile.Salary);

        dic.Add("Article", profile.Article);

 

        return dic;

    }

 

    /// <summary>

    /// 获取用户的指定的Profile

    /// </summary>

    /// <param name="properties">属性名称数组</param>

    /// <returns></returns>

    [WebMethod]

    public IDictionary<string, object> GetPropertiesForCurrentUser(string[] properties)

    {

        //Place code here.

        return null;

    }

 

    /// <summary>

    /// 保存用户的Profile

    /// </summary>

    /// <param name="values">用户的Profile的字典数据</param>

    /// <returns></returns>

    [WebMethod]

    public int SetPropertiesForCurrentUser(IDictionary<string, object> values)

    {

        //Place code here.

        ProfileCommon profile = (ProfileCommon)HttpContext.Current.Profile;

        profile.Age = Convert.ToInt32(values["Age"]);

        profile.Salary = Convert.ToInt32(values["Salary"]);

        profile.Article.Title = values["Article.Title"].ToString();

        profile.Article.PublishTime = Convert.ToDateTime(values["Article.PublishTime"]);

 

        return values.Count;

    }

}

对应的配置文件Profile如下:

                <profile enabled="true">

                        <properties>

                                <add name="Age" type="System.Int32"/>

                                <add name="Salary" type="System.Int32"/>

                                <group name="Article">

                                        <add name="Title" type="System.String" />

                                        <add name="PublishTime" type="System.DateTime" />

                                </group>

                        </properties>

                </profile>

完成自定义的用户个性化Web Service之后,若想在某个ASP.NET AJAX页面中使用该自定义的用户个性化服务,我们也需要在ScriptManager中显式声明:

<asp:ScriptManager ID="sm" runat="server">

    <ProfileService

        Path="~/CustomApplicationServices/CustomProfileService.asmx" />

</asp:ScriptManager>

其中粗体部分的<ProfileService />标签对应着ASP.NET AJAX服务器端的System.Web.UI.ProfileServiceManager类型,负责设置页面中自定义用户个性化Web Service的位置。将其Path属性设置为前面编写的CustomProfileService.asmx Web Service之后,该页面中的用户个性化服务将均由这个自定义的CustomProfileService.asmx处理。

提示:若是将<ProfileService />标签的Path属性设置为空字符串,或是不设置该属性,则ASP.NET AJAX仍会使用默认的用户个性化服务。

ProfileServiceManager类型还暴露出了一个属性:LoadProperties,表示希望随页面HTML代码的加载一起发送到客户端的用户个性化属性。我们可以为LoadProperties设置多个不同的用户个性化属性,属性之间用逗号分开。

17.        Asp.net Ajax构建异步调用的方式

17.1. Sys.Net.WebRequest

可以通过创建一个Sys.Net.WebRequest类型的对象来完成对服务器端某个页面的调用。如:

var getPage;

var postPage;

var displayElement;

 

function pageLoad()

{

    getPage = "GetTarget.xml";

    postPage = "PostTarget.aspx";

    displayElement = $get("resultId");

}

 

function GetWebRequest()

{

    // 创建WebRequest对象

    var wRequest = new Sys.Net.WebRequest();

   

    // url - 请求的url

    wRequest.set_url(getPage);

   

    // getResolvedUrl() - 转换为可用url

    alert(wRequest.getResolvedUrl());

   

    // httpVerb - HTTP请求方式“GET”或“POST”

    wRequest.set_httpVerb("GET");

         

    // userContext - 用户上下文

    wRequest.set_userContext("webabcd");

           

    wRequest.add_completed(OnWebRequestCompleted);

   

    displayElement.innerHTML = "";

              

    // invoke() - 执行请求

    wRequest.invoke();

}

// executor - WebRequestExecutor对象

function OnWebRequestCompleted(executor, e)

{       

    // responseAvailable - 请求是否成功完成

    if(executor.get_responseAvailable())

    {

        displayElement.innerHTML = "";

  

        // 显示Web Request的用户上下文

        // webRequest - 获得与当前executor相关的WebRequest对象

        // userContext - 用户上下文

        displayElement.innerHTML += "User Context:" + executor.get_webRequest().get_userContext();

        displayElement.innerHTML += "<br /><br />";

  

        // 显示Web Request的状态     

        // statusCode - 状态代码

        // statusText - 状态信息

        displayElement.innerHTML += "Status Code:" + executor.get_statusCode();

        displayElement.innerHTML += "Status Text:" + executor.get_statusText();

        displayElement.innerHTML += "<br /><br />";

 

        // 显示Web Request的所有Header         

        // getAllResponseHeaders() - 获得全部头信息

        displayElement.innerHTML += "Headers:" + executor.getAllResponseHeaders();

        displayElement.innerHTML += "<br /><br />";

       

        // 显示Web Request的指定Header

        // getResponseHeader() - 获得指定头信息

        displayElement.innerHTML += executor.getResponseHeader("Content-Type");

        displayElement.innerHTML += "<br /><br />";

       

        // 显示Web Request的Body                

        displayElement.innerHTML += "Body:";

        if (document.all)

        {

            // responseData - 响应当前请求的Body文本

            displayElement.innerText += ""r"n" + executor.get_responseData();

        }

        else

        {

            // Firefox

            displayElement.textContent += ""r"n" + executor.get_responseData();

        }

        displayElement.innerHTML += "<br /><br />";

       

        // 显示XML数据

        displayElement.innerHTML += "XML:";

        if (document.all)

        {

            // xml - 响应当前请求的XMLDOM对象

            displayElement.innerText += ""r"n" + executor.get_xml().xml;

        }

        else

        {

            // Firefox

            displayElement.textContent += ""r"n" + "首节点:" + executor.get_xml().documentElement.nodeName;

        }

    }

    else

    {

        // timedOut - 请求是否超时

        if (executor.get_timedOut())

        {

            alert("超时");

        }

        // aborted - 请求是否被终止

        else if (executor.get_aborted())

        {

            alert("请求被终止");

        }

    }

}

17.2. Sys.Net.WebServiceProxy

通常情况下,我们需要在页面中指定所要使用的WebService页面,利用Sys.Net.WebServiceProxy可以实现对于服务器端WebService页面和方法的后期邦定,从而实现了WebService的按需加载。

定义如下:

var webRequest = Sys.Net.WebServiceProxy.invoke(path, methodName, useHttpGet, parameters, succeededCallback, failedCallback, userContext, timeout);

17.3. Generated Proxy Classes

对于标识有[ScriptService]WebService[WebMethod]的方法,可以在客户端直接通过NameSpace.ClassName.MethodName(params,OnSucceed,OnFail,UserContext)来调用,也可以通过一种统一的方式来指定对应的回调处理函数、用户上下文和超时时间。如:

Samples.AspNet.UsingProxyClass.set_timeout(200);

Samples.AspNet.UsingProxyClass.set_defaultUserContext("Default context");

Samples.AspNet.UsingProxyClass.set_defaultSucceededCallback(SucceededCallback);

Samples.AspNet.UsingProxyClass.set_defaultFailedCallback(FailedCallback);

 

Samples.AspNet.UsingProxyClass.GetDefaultColor();

也可以采用创建实例的方式来调用:

    // Assign default values for the generated proxy using

    // a proxy instance.

    proxyInstance = new Samples.AspNet.UsingProxyClass();

    proxyInstance.set_timeout(1000);

    proxyInstance.set_defaultUserContext("New context");

    proxyInstance.set_defaultSucceededCallback(SucceededCallback);

    proxyInstance.set_defaultFailedCallback(FailedCallback);

 

    // Set the default color object.

    proxyInstance.SetColor(color); 

 

18.        RequestManager

18.1. PageRequestManager

PageRequestManager适用于包含有局部页刷新的场景中,既包含有UpdatePanel控件的页面中。

18.1.1.                     事件

Name

Description

beginRequest Event

Raised before processing of an asynchronous postback starts and the postback request is sent to the server.

endRequest Event

Raised after an asynchronous postback is finished and control has been returned to the browser.

initializeRequest Event

Raised during the initialization of the asynchronous postback.

pageLoaded Event

Raised after all content on the page is refreshed as the result of either a synchronous or an asynchronous postback.

pageLoading Event

Raised after the response from the server to an asynchronous postback is received but before any content on the page is updated.

 

这些事件触发的先后顺序如下:

PageRequestManager event

Description

initializeRequest

Raised before processing of the asynchronous request starts. You can use this event to cancel a postback.

beginRequest

Raised before processing of an asynchronous postback starts and the postback is sent to the server. You can use this event to set request headers or to begin an animation that indicates that the page is processing.

pageLoading

Raised after the response from the server to an asynchronous postback is received but before any content on the page is updated. You can use this event to provide a custom transition effect for updated content.

pageLoaded

Raised after all content on the page is refreshed, as a result of either a synchronous or an asynchronous postback. You can use this event to provide a custom transition effect for updated content.

endRequest

Raised after an asynchronous postback is finished and control has been returned to the browser. You can use this event to provide a notification to users or to log errors.

 

18.1.2.                     应用

我们可以利用上面的事件,针对所有的WebRequest做调用前和调用后的处理。

如:

            <script runat="Server">

                protected void Button1_Click(object sender, EventArgs e)

                {

                    int i = Int32.Parse("abc");

                }

 

                protected void Button2_Click(object sender, EventArgs e)

                {

                    ScriptManager.GetCurrent(this).RegisterDataItem(this.UpdatePanel1, "webabcd");

                }

            </script>

 

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

            </asp:ScriptManager>

            <asp:UpdatePanel ID="UpdatePanel1" UpdateMode="Conditional" runat="Server">

                <ContentTemplate>

                    <%= DateTime.Now.ToString() %>

                    <br />

                    <asp:Button ID="Button1" runat="server" Text="触发异常" OnClick="Button1_Click" />

                    &nbsp;&nbsp;

                    <asp:Button ID="Button2" runat="server" Text="RegisterDataItem测试" OnClick="Button2_Click" />

                </ContentTemplate>

            </asp:UpdatePanel>

            <p>

                <textarea id="TraceConsole" style="width: 500px; height: 100px;"></textarea>

            </p>

 

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

 

        Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(beginRequestHandler);

        Sys.WebForms.PageRequestManager.getInstance().add_endRequest(endRequestHandler);

        Sys.WebForms.PageRequestManager.getInstance().add_endRequest(endRequestHandler2);

 

                function beginRequestHandler(sender, e)

                {

                    // postBackElement - 产生回发事件的元素

                    alert(e.get_postBackElement().id);             

                }

                               

                function endRequestHandler(sender, e)

                {

                    // response - Sys.Net.WebRequestExecutor对象

                        Sys.Debug.trace("状态代码:" + e.get_response().get_statusCode());

       

                        if (e.get_error())

                        {

                            // errorHandled - 异常是否已经被处理

                        e.set_errorHandled(true);

                        // error - Error对象

                            Sys.Debug.trace("出错了!错误信息:" + e.get_error().message);

                        }

                }

               

                function endRequestHandler2(sender, e)

                {

                        var upId = "<%= this.UpdatePanel1.ClientID %>";

                       

                        // dataItems - 服务端注册的DataItem

                        if (typeof(e.get_dataItems()[upId]) != 'undefined')

                        {

                            Sys.Debug.trace("注册的数据项为:" + e.get_dataItems()[upId]);

                        }

                }

 

            </script>

 

18.2. Sys.Net.WebRequestManager

对于所有的Sys.Net.WebRequest的对象的调用,可以利用Sys.Net.WebRequestManager来进行统一的管理和设定。

var displayElement;

 

function pageLoad()

{

    displayElement = $get("ResultId");

   

    // defaultTimeout - 默认超时时间

    Sys.Net.WebRequestManager.set_defaultTimeout(500);

   

    Sys.Net.WebRequestManager.add_completedRequest(OnWebRequestCompleted);

       

    Sys.Net.WebRequestManager.add_invokingRequest

    (

        function (executor, e)

        {

            alert('调用请求前');

        }

    );

}

 

function GetData()

{

    // 创建WebRequest对象

    wRequest = new Sys.Net.WebRequest();

 

    // url - 请求的url

    wRequest.set_url("PostTarget.aspx");

     

    if (document.all)

    {

        displayElement.innerText = "";

    }

    else

    {

        // Firefox

        displayElement.textContent = "";

    }

  

    // invoke() - 执行请求

    wRequest.invoke();

   

    if (new Date().getSeconds() % 2 == 0)

    {

        // executor - 与当前WebRequest对象相关的executor

        var executor = wRequest.get_executor();

    

        // abort() - 终止请求

        executor.abort();

       

        // aborted - 与当前executor相关的请求是否被终止

        alert('请求是否被终止:' + executor.get_aborted());

    }

}

 

// executor - WebRequestExecutor对象

function OnWebRequestCompleted(executor, e)

{       

    // responseAvailable - 请求是否成功完成

    if(executor.get_responseAvailable())

    {

         // 显示Web Request的Body                

        displayElement.innerHTML += "Body:";

        if (document.all)

        {

            // responseData - 响应当前请求的Body文本

            displayElement.innerText += ""r"n" + executor.get_responseData();

        }

        else

        {

            // Firefox

            displayElement.textContent += ""r"n" + executor.get_responseData();

        }

        displayElement.innerHTML += "<br /><br />";

    }

    else

    {

        // timedOut - 请求是否超时

        if (executor.get_timedOut())

        {

            alert("超时");

        }

        // aborted - 请求是否被终止

        else if (executor.get_aborted())

        {

            alert("请求被终止");

        }

    }

}

18.3. 两者的区别

我们可以利用WebRequestManager对所有的服务器端的请求做统一的拦截处理,而PageRequestManager仅能对包含有UpdatePanel的涉及到页面局部刷新的场景使用。

19.        如何进行Ajax程序的调试

19.1. 传统的调试方式

19.1.1.                     取消“禁用脚本调试”项

步骤如下:

工具àinternet选项à高级à取消勾选“禁用脚本调试”。

19.1.2.                     加载要调试的程序源代码

注意由于此时我们进行的是客户端的调试,所以需要将服务器端生成的代码加载到我们的调试工具中,步骤如下:

查看à脚本调试程序à打开。

然后会要求您选择一个调试器,在您选择了一个调试器之后,就会将客户端的源代码加载到调试环境中,我们就可以在其中的javascript语句处设置断点,进行调试了。

19.2. 断言(Assert

    function btnAssert_onclick()

    {

        var a = 50;

        Sys.Debug.assert(a > 60, "a的当前值为:“" + a + "”,而我们需要它大于60");

        //显示具体的位置所在

        //Sys.Debug.assert(a > 60, "a的当前值为:“" + a + "”,而我们需要它大于60", true);

}

前者的截图如下:


后者的截图如下:


19.3. 进入调试(Fail

    function btnFail_onclick()

    {

        var a = 50;

       

        if ( a <= 60)

        {

            Sys.Debug.fail("a的当前值为:“" + a + "”,而我们需要它大于60");      

        }

    }

执行这段代码时,有程序主动地进入调试状态,截图如下:


19.4. 跟踪(Trace

使用跟踪的时候,需要页面中包含一个Id为“TraceConsole”的TextArea

        <!--id为“TraceConsole”的textarea用于显示Tracing信息-->

        <textarea id="TraceConsole" style="width: 500px; height: 100px;"></textarea>

使用Trace的代码如下:

    function btnTrace_onclick()

    {

        var a = 50;

       

        if ( a <= 60)

        {

            Sys.Debug.trace("a的当前值为:“" + a + "”,而我们需要它大于60");      

        }

}

19.5. 跟踪显示对象成员信息(TraceDump

利用该方法能够直接的显示出当前对象的各个成员的信息。

    function btnDump_onclick()

    {

        var person = {name:"Bill",age:23,gender:"Male",weight:120,height:180};

        Sys.Debug.traceDump(person,person.name);

    }


19.6. 清空跟踪信息(ClearTrace

利用该方法来清空TraceConsole文本框中的信息。

    function btnClear_onclick()

    {

        Sys.Debug.clearTrace()

    }

20.        Asp.Net Ajax的控件

20.1. updatePanel

20.1.1.                     RenderMode

RenderMode属性功能与Html标签的style.display属性作用一样, 只是UpdatePanel只有BlockInline两种方式。

20.1.2.                     UpdateMode

UpdateMode属性可以设置为AlwaysConditional两种方式. 默认情况下属性值为Always

Ø 如果设置为Conditional, 则只有当前UpatePanel内部的元素(比如button)提交时, 才能引起当前UpdatePanel更新;

Ø 如果设置为Always, 则不管点击UpdatePanel内部还是外部的按钮都会使当前UpdatePanel更新。

20.1.3.                     ChildrenAsTriggers

ChildrenAsTriggers属性可以设置为TrueFalse. 默认情况下属性值为True

如果设置为False, 则点击当前UpdatePanel中的元素不会引起当前UpdatePanel更新;但它可能会引起本UpdatePanel之外的页面局部更新。

20.1.4.                     Triggers

PostBackTrigger / AsyncPostBackTrigger标记可以指定当前UpdatePanel之外的元素来对自己进行更新。

两者的区别:一个是一般页面提交, 一个是异步无刷新提交。假如设置了UpdateMode="Conditional", 则只有点击当前UpdatePanel中的button才能更新本Panel中的内容. 如果想设置本UpdatePanel外的元素对本Panel内容进行更新, 则可以设置该属性。

20.2. updateProgress

属性如下:

Ø AssociatedUpdatePanelID表示由哪个UpdatePanel来使自己呈现;

Ø DynamicLayout表示UpdateProgress是否固定占有一定空间,即使是隐藏时; 如果该值为true, 则只有显示时才占用页面空间;

Ø DisplayAfter表示显示UpdateProgress内容之前需要等待的时间。

20.3. Timer

Ø 通过设置Interval(毫秒)可以定期的更新页面; 可以配合UpdateMode来禁止某些UpdataPanle不更新;

Ø 如果把Timer置于UpdatePanel外面, 可以非异步提交整个页面。

20.4. Validators

 Asp.net AjaxVS 2005自带的系列验证控件在使用时,会有些问题.Microsoft又推出一系列与原控件名称一一对应的控件集, bin目录的Validators.dll。使用方式和原来的方式一样。   

使用这些Validators时,需要修改配置文件如下:

                <pages>

                        <tagMapping>

                                <add tagType="System.Web.UI.WebControls.CompareValidator" mappedTagType="Microsoft.Web.UI.Compatibility.CompareValidator, Validators"/>

                                <add tagType="System.Web.UI.WebControls.CustomValidator" mappedTagType="Microsoft.Web.UI.Compatibility.CustomValidator, Validators"/>

                                <add tagType="System.Web.UI.WebControls.RangeValidator" mappedTagType="Microsoft.Web.UI.Compatibility.RangeValidator, Validators"/>

                                <add tagType="System.Web.UI.WebControls.RegularExpressionValidator" mappedTagType="Microsoft.Web.UI.Compatibility.RegularExpressionValidator, Validators"/>

                                <add tagType="System.Web.UI.WebControls.RequiredFieldValidator" mappedTagType="Microsoft.Web.UI.Compatibility.RequiredFieldValidator, Validators"/>

                                <add tagType="System.Web.UI.WebControls.ValidationSummary" mappedTagType="Microsoft.Web.UI.Compatibility.ValidationSummary, Validators"/>

                        </tagMapping>

                </pages>

21.         Asp.net Ajax 1.0相关部分

Ø AjaxControlToolkit,包含有很多asp.net ajax实用控件的集合。

Ø CTP全称“Community Test Preview”,是社区测试预览版,还没有正式发布。