《Ajax开发精要》关于ajaxanywhere 教程三

11.3.3 AjaxAnywhere的类库及其用法

AjaxAnywhere使用一个名为aa.jsJavascript文件来处理客户端的全部Ajax操作,包括初始化XMLHttpRequest、获取表单内容、发送Ajax请求、执行回调函数等。aa.js也是使AjaxAnywhere之前必须了解的,至少应该知道其经常用到的APIAjax Anywhere的官方网站提供了相应的Javascript Document,方便快速查找和了解这些API

1AjaxAnywhere的初始化

aa.js中定义了一个AjaxAnywhere对象,针对Ajax的各种操作被抽象成AjaxAnywhere对象的方法,通过这些对象方法完成所需的操作。必要的时候,可以重载这些方法,以便满足个性化的需求。在aa.js文件的末端,AjaxAnywhere对象使用默认的构造方法完成对象实例化。

ajaxAnywhere = new AjaxAnywhere();

ajaxAnywhere.bindById();

所以,所有引用aa.js的页面都可以在Javascript代码段中使用AjaxAnywhere对象的实例ajaxAnywhere

AjaxAnywhere初始化的时候,它在默认的构造函数中完成XMLHttpRequest对象的创建,并保存在AjaxAnywhere对象属性req中。AjaxAnywhere对象默认的构造方法如例程11-23所示。

例程11-23 AjaxAnywhere对象的默认构造方法

function AjaxAnywhere() {

this.id = AjaxAnywhere.defaultInstanceName;//id,用于生成更新区域的编号等用途

this.formName = null;//页面表单名称

this.notSupported = false;//是否支持Ajax

this.delayBeforeContentUpdate = true;//在更新页面内容之前是否延迟

this.delayInMillis = 100;//延迟时间

//初始化XMLHttpRequest对象--req

if (window.XMLHttpRequest) {

this.req = new XMLHttpRequest();

} else if (window.ActiveXObject) {

try {

this.req = new ActiveXObject("Msxml2.XMLHTTP");

} catch(e) {

try {

this.req = new ActiveXObject("Microsoft.XMLHTTP");

} catch(e1) {

this.notSupported = true;

/* XMLHTTPRequest not supported */

}

}

}

//确定浏览器是否支持Ajax

if (this.req == null || typeof this.req == "undefined")

this.notSupported = true;

}

2AjaxAnywhere处理Ajax请求

AjaxAnywhere提供两个公共方法处理Ajax请求的发送:submitAJAX(additionalPost Data, submitButton)getAJAX(url, zonesToRefresh)。前者用于发送POST类型的Ajax请求,后者则用于发送GET类型的请求,可以直接在Web页面的表单中或者Javascript代码段直接使用ajaxAnywhere.submitAJAX(additionalPostData, submitButton)或者ajaxAny where. getAJAX (url, zonesToRefresh)向服务器发送Ajax请求。

ajaxAnywhere对象的属性formName保存Ajax所指向的表单名称,只要为其指定表单名称(如果未指定,则默认是Web页面中的第一个表单),submitAJAX(additionalPost Data,submitButton)就能够自动获取指定表单的全部表单域及其值,组成parameterName1 =value1 &parameterName2=value2字符串,这个过程由私有(private)方法preparePostData (submitButton)完成;preparePostData(submitButton)方法遍历表单中的全部元素,将下拉列表、文本框、复选框、单选框等的值自动加入字符串中;submitAJAX方法的参数additionalPostData代表除了表单域值外还要发送给服务器的内容,submitButton则是代表发送操作是否由提交按钮触发的。SubmitAJAX()方法的代码如例程11-24所示。

例程11-24 submitAJAX() 方法发送POST类型请求

AjaxAnywhere.prototype.submitAJAX = function(additionalPostData, submitButton) {

//如果浏览器不支持Ajax

if (this.notSupported)

return this.onSubmitAjaxNotSupported(additionalPostData);

//附加参数为空

if (additionalPostData == null || typeof additionalPostData == "undefined")

additionalPostData = "";

//id绑定

this.bindById();

//获取当前表单对象

var form = this.findForm();

//获取表单的action,确定表单提交目标的url

var actionAttrNode = form.attributes.getNamedItem("action");

var url = actionAttrNode == null?null:actionAttrNode.nodeValue;

//如果表单action未设置,则url为当前页面

if ((url == null) || (url == ""))

url = location.href;

//确定请求成功后要重载刷新的页面区域

var zones = this.getZonesToReload(url, submitButton);

//如果未设置重载刷新区域,则刷新整个页面

if (zones == null) {

if (typeof form.submit_old == "undefined")

form.submit();

else

form.submit_old();

return;

}

//放弃上一次未完成的请求

this.dropPreviousRequest();

//设置请求参数,发送类型为POST,请求为异步方式

this.req.open("POST", url, true);

this.req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

this.req.setRequestHeader("Accept", "text/xml");

//确定要发送给服务器的内容

var postData = this.preparePostData(submitButton);

//已设置要重载刷新的区域,将区域名称附加在发送内容中

if (zones != "")

postData = '&aazones=' + encodeURIComponent(zones) + "&" + postData + "&" + additionalPostData;

else

postData += "&" + additionalPostData;

//发送Ajax请求

this.sendPreparedRequest(postData);

}

显然,如果使用AjaxAnywhere自定义标签为Web页面划分指定了刷新区域,则submitAJAX()方法也会将其包含在参数中发送到服务器端。

相对的,getAJAX(url,zoneToRefresh)方法则比较简单。它获取Ajax请求的目标URL,将需要发送的参数附着在这个URL后面,然后调用XMLHttpRequest对象的open()send()方法将其发送除去。getAJAX(url,zoneToRefresh)方法的代码如例程11-25所示。

例程11-25 getAJAX()方法发送GET类型请求

AjaxAnywhere.prototype.getAJAX = function(url, zonesToRefresh) {

//如果浏览器不支持Ajax,则刷新整个页面

if (this.notSupported)

return this.onGetAjaxNotSupported(url);

//id绑定

this.bindById();

//如果为设置刷新区域,则刷新整个页面

if (zonesToRefresh == null || typeof zonesToRefresh == "undefined")

zonesToRefresh = "";

var urlDependentZones = this.getZonesToReload(url);

if (urlDependentZones == null) {

location.href = url;

return;

}

//已经设置多个刷新区域

if (urlDependentZones.length != 0)

zonesToRefresh += "," + urlDependentZones;

//放弃上一次未完成的额请求

this.dropPreviousRequest();

//确定请求附加的参数

url += (url.indexOf("?") != -1) ? "&" : "?";

url += "aa_rand=" + Math.random();

// avoid caching

if (zonesToRefresh != null && zonesToRefresh != "")

url += '&aazones=' + encodeURIComponent(zonesToRefresh);

//设置请求参数,发送类型为GET,响应结果为XML文档

this.req.open("GET", url, true);

this.req.setRequestHeader("Accept", "text/xml");

//发送Ajax请求

this.sendPreparedRequest("");

}

与一般的Ajax应用程序一样,AjaxAnywhere更新页面的操作仍然由回调函数完成,这也是Ajax的机制所定义的。应该注意到,submitAJAX()方法和getAJAX()方法的后面都调用了一个sendPreparedRequest()的方法,只是两者传入的参数内容有所不同而已。AjaxAnywhere对象在该方法中设置必要的请求头信息,为XMLHttpRequest对象指定回调函数,然后才将Ajax请求发送出去。setPreparedRequest()方法如例程11-26所示。

例程11-26 setPreparedRequest()方法

AjaxAnywhere.prototype.sendPreparedRequest = function (postData) {

//确定Ajax请求回调函数

var callbackKey = this.id + "_callbackFunction";

if (typeof window[callbackKey] == "undefined")

window[callbackKey] = new Function("AjaxAnywhere.findInstance (\"" + this.id + "\").callback(); ");

this.req.onreadystatechange = window[callbackKey];

//显示请求正在处理的提示信息

this.showLoadingMessage();

//设置请求头参数,当前请求为aaxml请求

this.req.setRequestHeader("aaxmlrequest", "true");

//发送请求

this.req.send(postData);

}

从这个方法可以看出,如果未在Web页面中定义相应的回调函数,则AjaxAnywhere将使用默认的回调函数。

AjaxAnywhere统一使用XML文档来组织服务器返回的响应数据。这个文档采用UTF-8的编码方式,例程11-22所配置的AAFilter也采用UTF-8的方式从请求中获取请求数据。AjaxAnywhere支持页面区域内容刷新、URL跳转、脚本、图像等处理功能,这些处理功能的信息都包含在这个XML文档中。当客户端发送的Ajax请求被正常执行后,AjaxAnywhere即将返回的响应数据组织为符合例程11-27所示文档类型定义文件DTD所规定范式的XML文档。

例程11-27 AjaxAnywhere返回的XML文档的文档类型定义DTD

<?xml version="1.0" encoding="UTF-8" ?>

<!ELEMENT zones ( document*, zone*, script*, exception*, redirect*, image* ) >

<!ELEMENT document (#PCDATA)>

<!ELEMENT zone ( #PCDATA ) >

<!ATTLIST zone name NMTOKEN #REQUIRED >

<!ELEMENT script ( #PCDATA ) >

<!ELEMENT exception ( #PCDATA ) >

<!ATTLIST exception type NMTOKEN #REQUIRED >

<!ELEMENT redirect ( #PCDATA ) >

<!ELEMENT image (#PCDATA) >

例程11-28是一个仅包含zonescriptexceptionredirect节点的示范XML文档。这个文档由AjaxAnywhere的业务类自动组织,与Web应用程序的其他实体类、业务类无关。Web应用程序只需要按照传统的通信机制一样,将响应结果输出即可。

例程11-28 AjaxAnywhere返回的包含响应数据的XML文档

<?xml version="1.0" encoding="UTF-8"?>

<zones>

<zone name="stateSavingScript"/>

<zone name="countriesList">

<![CDATA[

<select size="10" name="country">

<option value=IN>India</options>

</select>

]]>

</zone>

<script>

<![CDATA[

var states = document.getElementsByName("jsf_state_64")

var trees = document.getElementsByName("jsf_tree_64")

if(states!=null)

for(var i=0;i<states.length;i++)

if(states[i].tagName.toLowerCase()=="input") {

states[i].values="null";

}

if(trees!=null)

for(var i=0;i<trees.length;i++)

if(trees[i].tagName.toLowerCase()=="input")

trees[i].values="null";

]]>

</script>

<script/>

<script/>

<exception type="org.apache.jasper.JasperException"><![CDATA[

org.apache.jasper.JasperException:AjaxAnywhere demo exception

at org.apache.jasper.servlet.JspServletWraper.service (Jsp Servlet Wrapper.java:372)

............

]]>

</exception>

<redirect><![CDATA[reditected.jsp]]></redirect>

</zones>

AjaxAnywhere默认的回调函数使用XMLHttpRequest对象的responseXML()方法来获取服务器返回的这个XML文档,使用DOM解析。如果XML文档包含documentzone元素,则按照之前设定的更新区域来更新页面中指定区域的内容。如果XML文档中包含script元素,则从元素中提取脚本,使用eval()方法执行。如果服务器响应请求的时候发生系统异常或者HTTP请求异常,则XML文档中会包含exception元素,回调函数即可从元素中获取异常信息,交给this.handleException (type, details)方法做异常处理。如果XML文档中包含redirect元素,则回调函数将其转化为“locarion.href=newURL”的方法,将页面跳转到指定的URL。如果XML文档中包含image元素,则AjaxAnywhere即从服务器获取image对象所指定的图像,将其缓存在浏览器。AjaxAnywhere默认的回调函数如例程11-29所示。

例程11-29 AjaxAnywhere对象默认的构造函数

AjaxAnywhere.prototype.callback = function() {

if (this.req.readyState == 4) {

this.onBeforeResponseProcessing();

this.hideLoadingMessage();

if (this.req.status == 200) {

if (this.req.getResponseHeader('content-type').toLowerCase(). substring(0, 8) != 'text/xml')

alert("AjaxAnywhere error : content-type in not text/xml : [" + this.req.getResponseHeader('content-type') + "]");

var docs = this.req.responseXML.getElementsByTagName("documen t");

var redirects = this.req.responseXML.getElementsByTagNa me ("redirect");

var zones = this.req.responseXML.getElementsByTagName("zon e");

var exceptions = this.req.responseXML.getElementsByTagName ("exception");

var scripts = this.req.responseXML.getElementsByTagName("scr ipt");

var images = this.req.responseXML.getElementsByTagName("imag e");

if (redirects.length != 0) {

var newURL = redirects[0].firstChild.data;

location.href = newURL;

}

if (docs.length != 0) {

var newContent = docs[0].firstChild.data;

//cleanup ressources

delete this.req;

document.close();

document.write(newContent);

document.close();

}

if (images.length != 0) {

var preLoad = new Array(images.length);

for (var i = 0; i < images.length; i++) {

var img = images[i].firstChild;

if (img != null) {

preLoad[i] = new Image();

preLoad[i].src = img.data;

}

}

if (this.delayBeforeContentUpdate) {

delay(this.delayInMillis);

}

}

if (zones.length != 0) {

for (var i = 0; i < zones.length; i++) {

var zoneNode = zones[i];

var name = zoneNode.getAttribute("name");

var fc = zoneNode.firstChild;

var html = (fc == null)?"":fc.data;

var zoneHolder = document.getElementById("aazone." + name);

if (zoneHolder != null && typeof(zoneHolder) != "undefined") {

zoneHolder.innerHTML = html;

}

}

}

if (exceptions.length != 0) {

var e = exceptions[0];

var type = e.getAttribute("type");

var stackTrace = e.firstChild.data;

this.handleException(type, stackTrace);

}

if (scripts.length != 0) {

for (var $$$$i = 0; $$$$i < scripts.length; $$$$i++) {

// use $$$$i variable to avoid collision with "i" inside user script

var script = scripts[$$$$i].firstChild;

if (script != null) {

script = script.data;

if (script.indexOf("document.write") != -1) {

this.handleException("document.write", "This script contains document.write(), which is not compatible with AjaxAnywhere : \n\n" + script);

} else {

eval(script);

}

}

}

var globals = this.getGlobalScriptsDeclarationsList(scr ipt);

if (globals != null)

for (var i in globals) {

var objName = globals[i];

try {

window[objName] = eval(objName);

} catch(e) {

}

}

}

} else {

if (this.req.status != 0)

this.handleHttpErrorCode(this.req.status);

}

this.restoreSubstitutedSubmitButtons();

this.onAfterResponseProcessing();

}

}

值得注意的是,在例程11-29所示的默认回调函数中,除了解析XML文档外,在回调函数执行业务逻辑之前和之后,还分别调用了两个可供重载的方法:this.onBeforeResp onse Processing() this.onAfterResponseProcessing()。如果希望在执行回调函数更新页面内容之前处理额外的业务,则可以在适当的位置重载this.onBeforeResponseProcessing()方法。如果希望执行完回调函数更新页面内容之后,还希望继续执行其他逻辑操作,则可以在适当的位置重载this.onAfterResponseProcessing()方法。从aa.js的源码中,可以看到目前这两个方法都未执行任何操作,如例程11-30所示。

例程11-30 onBeforeResponseProcessing()onAfterResponseProcessing()方法

/**

* Override this method to implement a custom action

*/

AjaxAnywhere.prototype.onBeforeResponseProcessing = function () {

};

/**

* Override this method to implement a custom action

*/

AjaxAnywhere.prototype.onAfterResponseProcessing = function () {

};

你可能感兴趣的:(JavaScript,xml,应用服务器,Ajax,prototype)