技术开发 频道

详解Ajax中XMLHttp请求及利弊分析

{$PageTitle=绠€浠媫绠€浠?/string>{$PageTitle=绠€浠媫绠€浠?/string>{$PageTitle=绠€浠媫绠€浠?/string>{$PageTitle=绠€浠媫绠€浠?/string>简介
当Microsoft开始在Internet Explorer 5.0中引入对XML基本的支持时,也引入了一个称为MSXML的ActiveX库,此库中的一个对象很快便成为广为人知的——XMLHttp。
XMLHttp对象最初是为了让开发者能在应用程序中的任何地方都能初始化HTTP请求而创造出来的,这些请求倾向于返回XML,所以XMLHttp对象提供了一个非常简单的方法用于以XML文档的方式访问所需信息。由于从一开始它就是一个ActiveX控件,因此XMLHttp不但可用于网页,也可应用于任何基于Windows的应用程序;然而,显然它在互联网上的受欢迎程序,要大大超出在桌面程序方面。
为紧跟潮流,Mozilla也在它的浏览器Firefox中模仿了XMLHttp功能,但不久后,Safari(从版本1.2开始)和Opera(版本7.6)浏览器也相继模仿了Mozilla的功能实现。今天,这四大浏览器都已在某种程度上支持XMLHttp(Safari和Opera在功能实现上仍不完整,除了GET和POST,不支持其他类型的请求)。

创建一个XMLHttp对象
显然,使用一个XMLHttp对象的第一步,是要创建它,因为Microsoft对此的实现是ActiveX控件,所以你也必须在JavaScript中使用ActiveXObject的所有者类,并传递给它一个XMLHttp控件的签名:

var oXmlHttp = new ActiveXObject("Microsoft.XMLHttp");

上述代码创建了XMLHttp对象的第一个版本(其与IE 5.0一同发布),问题是,随着后续MSXML库的发布,也发布了好几个不同的新版本,每次发布都带来了更好的稳定性与更快的速度,所以,必须要确定你使用的是用户电脑上可用的最新版本。每个版本的签名如下:

Microsoft.XMLHttp
MSXML2.XMLHttp
MSXML2.XMLHttp.3.0
MSXML2.XMLHttp.4.0
MSXML2.XMLHttp.5.0

不幸的是,确定可用非常好的版本唯一的方法,是依次尝试创建它们。因为其本身为一个ActiveX控件,任何创建对象上的失败,都会抛出一个错误,这意味着你必须在一个try…catch块中编写这些代码,如下:

function createXMLHttp() { var aVersions = [ "MSXML2.XMLHttp.5.0", "MSXML2.XMLHttp.4.0","MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp","Microsoft.XMLHttp" ]; for (var i = 0; i < aVersions.length; i++) { try { var oXmlHttp = new ActiveXObject(aVersions[i]); return oXmlHttp; } catch (oError) { //Do nothing } } throw new Error("未安装MSXML!"); }
函数createXMLHttp()中保存了一个XMLHttp签名的数组,经常使用的靠前,并遍历此数组以尝试用每个签名创建一个XMLHttp对象。如果创建失败,catch语句将会捕捉到一个JavaScript错误,并尝试下一个签名。当对象被创建后,它将被返回给调用者,如果函数结束时仍没有创建一个XMLHttp对象,将会抛出一个错误指出创建失败。
幸好,在以上浏览器中创建一个XMLHttp对象还算是比较容易的,Mozilla Firefox、Safari和Opera都可以使用下面这行代码:

var oXmlHttp = new XMLHttpRequest();
自然地,它以一种跨浏览器的方式帮助开发者创建XMLHttp对象。你还可以修改前面定义过的createXMLHttp()函数,生成一个如下的函数:

function createXMLHttp() { if (typeof XMLHttpRequest != "undefined") { return new XMLHttpRequest(); } else if (window.ActiveXObject) { var aVersions = [ "MSXML2.XMLHttp.5.0", "MSXML2.XMLHttp.4.0","MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp","Microsoft.XMLHttp" ]; for (var i = 0; i < aVersions.length; i++) { try { var oXmlHttp = new ActiveXObject(aVersions[i]); return oXmlHttp; } catch (oError) { //Do nothing } } } throw new Error("不能创建XMLHttp对象!"); }
现在,这个函数先检查是否定义了一个XMLHttpRequest类(使用typeof操作符),如果类XMLHttpRequest存在,它将被用于创建XMLHttp对象;否则,函数检查ActiveXObject类是否存在,如果存在,就执行一遍为IE创建XMLHttp对象相同的过程。如果以上两个测试都失败了,就会抛出一个错误。
另一种创建跨浏览器XMLHttp对象的方法,就是使用已为跨浏览器编写的类库:zXml。它由两位作者编写,可从http://www.nczonline.net/downloads/处下载,库中定义了一个创建XMLHttp对象的单一函数:

var oXmlHttp = zXmlHttp.createRequest();

函数createRequest与zXml库本身,将会贯穿全文,以帮助处理有关跨浏览器的Ajax技术。

在创建完一个XMLHttp对象之后,便可准备从JavaScript中发起HTTP请求。首先调用open()方法,由其完成对象的初始化,此方法接受以下三个参数:

1、 请求类型:指示某种请求类型的字符串,GET或POST(这也是目前所有浏览器都支持的类型)。
2、 URL:请求将发送到的某URL。
3、 异步(Async):一个布尔值,由其决定请求是否异步。

最后一个代表异步的参数,在此非常重要,因为它控制了JavaScript怎样执行请求。当设置为true时,请求被异步发送,JavaScript代码不会等待响应而继续执行,必须设置一个事件处理函数以监视对请求的响应。如果设置为false,请求被同步发送,而JavaScript会在继续执行代码之前等待来自服务器的响应;这意味着如果响应需要很长时间,那么用户将不能操作浏览器,直至响应完成。因此,开发Ajax应用程序最好的方法是,对数据的常规返回操作,尽量使用异步请求,而同步请求用于发送和接收与服务器之间的短小信息。
如对某一info.txt文件的异步GET请求,代码如下:

var oXmlHttp = zXmlHttp.createRequest(); oXmlHttp.open("get", "info.txt", true);

请注意此处的第一个参数,也就是请求类型,即使在技术层面要求请求类型全部为大写字符,但此处使用小写也是没关系的。 接下来,你需要定义一个onreadystatechange事件处理函数,XMLHttp对象有一个名为readyState的属性,当发送请求或收到响应时,此属性值会改变。以下是属性readyState可取的五个值:

0(未初始化):对象已创建,但还未调用open()方法。
1(正在加载):已调用open()方法,但请求还未发送。
2(已加载):请求已经发送。
3(相交互):已接收到一部分响应。
4(完成):所有数据已收到,连接已经关闭。

在每次readystate属性值变化时,都会引发readystatechange事件,并调用onreadystatechange事件处理函数。因为各浏览器实现之间的差异,在跨浏览器开发时唯一可靠的readystate值为0、1和4。在大多数情况下,当请求被返回时,只需检查此值是否为4:

var oXmlHttp = zXmlHttp.createRequest(); oXmlHttp.open("get", "info.txt", true); oXmlHttp.onreadystatechange = function () { if (oXmlHttp.readyState == 4) { alert("得到响应!"); } };
最后一步是调用send()方法,实际上是由它来发送请求的。这个方法只接受单一的参数——一个代表请求体的字符串。如果请求不需要一个请求体(比如说:GET请求就不需要),就必须传递给它一个null值:

var oXmlHttp = zXmlHttp.createRequest(); oXmlHttp.open("get", "info.txt", true); oXmlHttp.onreadystatechange = function () { if (oXmlHttp.readyState == 4) { alert("得到响应!"); } }; oXmlHttp.send(null);
大体流程是,发送请求后,当收到响应,将会显示一个提示框,但仅仅显示一条请求已被接收的消息是远远不够的,XMLHttp的真正威力在于,不但可以让你访问到返回的数据,还可以访问到响应状态和响应头部信息。
为得到从请求返回的数据,可使用responseText和responseXML属性;属性responseText返回一个包含请求体的字符串,而responseXML则是一个XML文档对象,只用于返回数据中包含有text/xml内容的情况。因此,为取得info.txt中的文本,必须进行以下调用:

var sData = oXmlHttp.responseText;
注意,只有文件存在,并且没有错误发生时,才会返回info.txt中相应的文本。但如果info.txt文件不存在,则responseText将返回一个服务器404错误信息,幸好,还有一个方法可以确定是否有错误发生。
属性status中包含有响应返回的HTTP状态代码,而statusText是关于状态的文本描述(如:OK或者Not Found)。使用这两个属性,你可以确认所接收到的数据是实际想要的数据,或告诉用户为什么数据没有收到:

if (oXmlHttp.status == 200) { alert("返回的数据为:" + oXmlHttp.responseText; } else { alert("错误为:" + oXmlHttp.statusText; }
通常来说,必须保证响应的status(状态码)为200,以表明请求完全成功,有时即使服务端有错误发生,属性readState仍会被设为4,所以仅仅检查此值还是不够的。在这个例子中,如果status值为200,将显示responseText的内容,否则,就显示一个出错信息。
在Opera中,仍未实现statusText属性,在其他的浏览器上,有时还会返回一个不精确的描述信息,所以,不能仅仅依靠statusText来判断是否有错误发生。
正如前面所提到的,我们还可以访问到响应头部信息,在此使用getResponseHeader()方法,并传递给它一个你想要的头部名,来得到一个特定的头部值。而当中最有用的一个响应头部信息是Content-Type,它将告诉你正在被发送的数据类型:

var sContentType = oXmlHttp.getResponseHeader("Content-Type"); if (sContentType == "text/xml") { alert("接收到的内容为XML!"); } else if (sContentType == "text/plain") { alert("接收到的内容为纯文本!"); } else { alert("无法判断的内容!"); }
这段代码检查响应中的内容类型,并用一个对话框来显示返回的数据类型。一般来说,从服务器接收到的数据,只会是XML(内容类型为text/xml)或者纯文本(内容类型为text/plain),因为这些类型最容易与JavaScript协同工作。
如果你更想看到从服务器返回的所有头部信息,那么可用getAllResponseHeaders()方法,其将返回一个包含所有头部信息的字符串,由换行符(JavaScript中表示为\n)或回车换行符(JavaScript中表示为\r\n)进行分隔,可以像如下所示对个别的头部信息进行处理:

var sHeaders = oXmlHttp.getAllResponseHeaders(); var aHeaders = sHeaders.split(/\r?\n/); for (var i=0; i < aHeaders.length; i++) { alert(aHeaders[i]); }
这个例子使用JavaScript的split()字符串方法,并传递给它一个正则表达式(由它来匹配回车换行或仅仅是换行),把包含头部信息的字符串分离到一个数组中。现在,你可以遍历这些头部信息,做一些想做的事了。别忘了,数组aHeaders中每一个字符串的形式为:(头部名:值)。
在头部信息被发送出去之前,也可以对头部信息进行修改。你也许想要表明你正在发送的数据内容是什么类型,或者你想要发送一些服务器可能需要与请求一并处理的其他额外数据,要这样做的话,在调用send()之前,必须先调用setRequestHeader()方法:

var oXmlHttp = zXmlHttp.createRequest(); oXmlHttp.open("get", "info.txt", true); oXmlHttp.onreadystatechange = function () { if (oXmlHttp.readyState == 4) { alert("得到响应!"); } }; oXmlHttp.setRequestHeader("myheader", "myvalue"); oXmlHttp.send(null);
在以上代码中,一个名为myheader的头部名在发送之前,被加入到请求中,在此是以myheader:myvalue的形式加入到默认头部信息中的。
由此,就可以处理异步请求了,而异步请求对大多数情况都是适用的。发送异步请求意味着你不需要分配一个onreadstatechange事件处理函数,因为当send()方法返回时,就会收到响应。所以,代码可以这样写:
var oXmlHttp = zXmlHttp.createRequest(); oXmlHttp.open("get", "info.txt", false); oXmlHttp.send(null); if (oXmlHttp.status == 200) { alert("返回的数据为:" + oXmlHttp.responseText; } else { alert("错误为:" + oXmlHttp.statusText; }
异步发送请求(设置open()的第三个参数为false),可以使你在调用send()之后,就立即开始评估响应,如果你想让用户等待响应,或者你只期望收到一小点数据(例如,小于1k),那就另当别论了,但在一般数据量或大数据量的情况中,最好都使用异步调用。
0
相关文章