电子邮件:changhong94@gmail.com
(本文登载于《电脑编程技巧与维护》2007年第8期,请勿转载)
摘要:本文通过利用ASP.NET/C#和Java两种服务器端语言,结合Html、JavaScript、XML等相关技术,演示了如何制作一个小型Ajax应用框架,并由此介绍了Ajax的核心技术和框架式开发技巧。
关键词:Ajax 框架式开发 网页应用程序 JavaScript XML
一, 前言
从2005年开始,Ajax技术在互联网应用编程方面如火如荼,众多网站纷纷改版采用Ajax技术实现用户不刷新页面的实时操作,甚至利用Ajax技术制作的界面类似于Windows的网络操作系统也逐步从设想走向实现。实际上,Ajax本身并非很高深的技术,其核心就是JavaScript中的XMLHttpRequest对象,只要掌握了这个对象的生成、发送数据、接收数据这几个为数不多的方法,就可以做出Ajax网页应用程序了。
Ajax的标志,就是浏览器页面不刷新,浏览器客户端直接从服务器端获取数据。根据这个特点,我们完全可以做出一个类桌面应用程序的互联网应用程序,用户以桌面应用程序的操作方式来使用,同时略去了繁复且易出错的安装过程。将桌面应用程序的强大和互联网应用程序的灵便接合在一起,这可能就是Ajax技术革命性意义之所在吧。但Ajax既然当了浏览器服务器和客户端之间的连接器,就要求我们要把服务器端代码、客户端代码和Ajax相关代码有机的组织起来,形成一个合理的开发框架。
目前已出现不少优秀的Ajax框架,有Sajax, SWATO, 还有著名的Google AJAXSLT框架,当然微软也不甘落后,给它的Ajax框架命名为Atlas,支持控件拖放的可视化开发,使用十分方便。但要使用这些框架,先要花不少时间学习,而且这些框架经过了高度封装,早就失去了XMLHttpRequest这核心对象的原本模样,如果遇到一个原有框架不能完成的开发任务,岂不是要束手无策。因此,要真正掌握Ajax技术,我们自制一个小型的Ajax框架,了解了Ajax技术的精髓,再去使用那些成熟框架不迟。
当然,框架制作要求开发人员知识面比较宽广,本文制作的Ajax框架,就用到了Html、JavaScript、XML等相关技术,需要读者对这些技术,至少对其概念要有一定的了解。而服务器端编程语言,选择了比较有代表性的ASP.NET/C#和Java两种,只要使用过这两种编程语言中的一种,就可以和笔者一起制作出一个小型的Ajax应用框架了。
二,框架示例应用
我们即将制作出的Ajax框架,需要在实际应用中经受检验。因此,我们将框架应用于一个相当常见的用户场景:用户登录验证。页面如图一所示:
(图一,用户登录页面)
本示例应用中,设定有效的用户名为user,密码为123。其它用户名和密码均不能通过登录验证。
应用场景如下:(读者自制好框架后,也请按下列步骤进行测试)
1. 填入用户名aaa,密码aaa,点击登录按钮,经服务器验证后,浏览器提示“用户名、密码错误,不能登录”。如图二所示:
(图二,输入错误的用户名、密码,经服务器端验证后返回错误信息)
2. 填入用户名user,密码123,点击登录按钮,经服务器验证后,页面转为已登录状态,并对用户致欢迎词。如图三所示:
(图三,输入正确的用户名、密码,经服务器端验证后通过登录)
3. 点击登出按钮,页面转换为原始的未登录状态,界面恢复为图一的界面,允许用户重新输入用户名和密码进行登录操作。
从步骤1到步骤3实现了一个从登录到登出的循环。读者根据下文的介绍制作好框架后,或针对本文所附的框架代码,可进行多次循环操作,测试操作过程中是否出现异常现象。
二,框架工作流程
登录、登出等每一个操作背后,都有一个浏览器客户端发出数据、服务器端根据数据进行相应处理、服务器端向客户端返回数据的完整流程。以下我们对工作流程进行分解并说明。
1. 用户浏览应用网页。此页面中,要包含Ajax核心对象XMLHttpRequest及其发送和接受数据的方法。
2. 对页面进行操作,触发相应的事件,通过事件调用客户端JavaScript函数生成需要向服务器发送的数据,并由XMLHttpRequest进行发送。在数据中,要指明是由服务器端的哪个类来进行数据处理。
3. 服务器端的“数据中心”接收到数据,要进行数据的分发,将数据发往其指明的数据处理类。数据处理类对数据进行处理后,将相应结果分为服务器端处理数据和客户端处理数据并发回数据中心。数据中心接收结果数据,处理需要在服务器端运行的部分,而将需要客户端处理的数据发回浏览器客户端。
4. 客户端接受到结果数据,根据数据进行相应处理。
以上流程的每个环节,我们在下文中都会进行代码的分析和编写,也请读者先思考一下自己会如何编写这个框架,然后与下文的代码进行对照。
三,ASP.NET代码实现
笔者使用的开发工具Microsoft Visual Studio 2005,服务器端开发语言为C#,但我们要制作的框架本身对开发工具要求不高,读者也可以使用Visual Studio 2003,但本文所附的示例代码就无法直接打开了。
以下将按照框架工作流程的4个步骤制作Ajax小型框架,并对代码进行一定的解释。
1. 建立项目并制作网页
首先打开Visual Studio,新建一个网站项目,可取名为AjaxFramework。在项目中,添加一个新网页,命名为AjaxFramework.htm。此网页内容为登录界面,以测试框架功能。其画面效果,就如上文图一所示。网页主体的Html代码如下:
<div id="log">
<div id="logon">
<label id="labelUsername">用户名:</label>
<input type="text" id="username" />
<label id="labelPassword">密码:</label>
<input type="password" id="password" />
<input type="button" id="buttonLogon" value="登录" />
</div>
<div id="logoff">
<label id="userInfo"></label>
<input type="button" id="buttonLogoff" value="登出" />
</div>
</div>
代码省略了Html文档主体之外的部分。要查看全部代码,请查看本文所附示例文档中的同名文件AjaxFramework.htm。
这一段代码体现了网页功能设计与界面设计分离的思路,只有我们实现登录登出功能所需要的界面元素代码,其中输入用户名的文本框为<input type="text" id="username" />,由于其id为username,网页中的JavaScript代码获得此文本框内容的语句为document.getElementById("username").value。同理,读者分析代码可以看到,要获得密码输入框内容的JavaScript语句则可以为document.getElementById("password").value。而要将上述代码正确显示为图一的模样,则是通过引入示例文档中的文件StyleSheet.css。
要让网页实现Ajax功能,需要通过JavaScript代码引入Ajax核心对象XMLHttpRequest并实现其生成对象、发送数据、接收数据的功能。新建JavaScript文件AjaxManage.js,在网页AjaxFramework.htm中通过
<script type="text/javascript" src="AjaxManage.js"></script>
引入。AjaxManage.js的代码如下:
var xmlHttp;//在网页中定义此Ajax核心对象,达到不刷新页面发送和接收服务器端数据
//生成Ajax核心对象的函数,此方法代码来自于《Ajax基础教程》一书
function createXMLHttpRequest() {
if (window.ActiveXObject) {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");//如果为IE浏览器,用此方法获得Ajax核心对象
}
else if (window.XMLHttpRequest) {
xmlHttp = new XMLHttpRequest();//如果为Firefox等非IE浏览器,用此方法获得Ajax核心对象
}
}
//通过Ajax核心对象发送数据的函数,数据内容为参数xml
function sendRequest(xml){
createXMLHttpRequest();//生成Ajax核心对象
var url="Handler.aspx";//发送数据的目的地
//发送数据
xmlHttp.open("POST",url,true);
xmlHttp.onreadystatechange = handleStateChange;
xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlHttp.send(xml);
}
//通过Ajax核心对象接收数据的函数
//向服务器端发送数据后,服务器端进行处理并将处理结果数据发回。
//客户端接收到数据时,会引发Ajax核心对象状态的改变。此时调用parseResults函数处理数据
function handleStateChange() {
if(xmlHttp.readyState == 4) {
if(xmlHttp.status == 200) {
parseResults();
}
}
}
//此函数处理服务器端返回的数据
function parseResults() {
var xmlDoc=xmlHttp.responseXML;//要求服务器端返回的数据为XML文档
//以下根据XML文档中的operation数据进行相应的处理
var nodes=xmlDoc.getElementsByTagName("operation");
for(var i=0;i<nodes.length;i++){
var node=nodes[i];
if(node.getAttribute("type")=="executeJsText"){ //指定处理类型为executeJsText,则采用eval方法运行数据中包含的JavaScript代码
eval(node.getAttribute("content"));
}else if(node.getAttribute("type")=="alert"){ //指定处理类型为alert,则在网页中弹出提示框
alert(node.getAttribute("content"));
}//根据实际应用需要,读者可在此处写下更多的处理类型并进行相应的处理操作
}
}
以上代码中parseResults()函数中应用了JavaScript的eval()函数。eval()函数可以在运行时动态执行程序根据需要生成的代码,形式为eval(code),将参数code作为程序语句执行,如下例:
var code=”alert(‘ok’);”;
eval(code); //等同于执行alert(“ok”);,程序将弹出对话框
在本框架中,服务器端生成不同的JavaScript代码放入xml数据中返回,客户端取出代码,由eval()函数执行,从而体现了高度的灵活性。
2. 为页面编写事件处理元素。
刚才制作的AjaxFramework.htm还只是一个静态页面,不能与用户产生互动。我们还是通过JavaScript代码来实现:用户点击登录按钮,页面引发按钮onclick事件,执行logon()函数。在logon()函数中,先由createLogonXml()函数根据页面用户的录入生成xml数据字符串,再调用AjaxManage.js中的sendRequest()将xml数据发往服务器端。
首先在AjaxFramework.htm的页面中加入如下代码:
<script type="text/javascript">
var varUserName="";
//但点击登录按钮时,调用logon()函数处理,同时预先指定服务器端处理的类为LogonHandler
document.getElementById("buttonLogon").onclick=function(){logon('LogonHandler');};
//但点击登出按钮时,调用logoff()函数处理,同时预先指定服务器端处理的类为LogoffHandler
document.getElementById("buttonLogoff").onclick=function(){logoff('LogoffHandler');};
</script>
这段代码指定了页面中登录和登出按钮发生点击时将要执行的代码。这段代码应置于html文档的最后,否则浏览器还未载入buttonLogon元素就未其指定onclick事件处理,将会引发错误。
为页面元素指定事件处理还有一种方法为:
<input type="button" id="buttonLogon" value="登录" onclick="function(){logon('LogonHandler');}" />
但笔者还是建议大家编写网页应用时将页面元素、编程代码和界面显示设计代码充分分离,实现程序制作各类专业人员的分工。
新建JavaScript文件AjaxFramework.js,在网页AjaxFramework.htm中通过
<script type="text/javascript" src="AjaxFramework.js"></script>
引入。AjaxFramework.js的作用就是放置AjaxFramework.htm网页中需要用到的JavaScript各个函数的代码,其内容如下:
//logon的代码
function logon(handler){
//在此处写一些逻辑判断代码,如用户名、密码是否为空等
sendRequest(createLogonXml(handler));
document.getElementById("password").value="";//安全起见,将密码清空
}
//创建将往服务器端发送的xml数据
function createLogonXml(handler){
var result="";
result+="<request>";
result+="<handler>";
result+=handler;
result+="</handler>";
result+="<parameters>";
result+="<parameter UserName='"+document.getElementById("username").value+"' Password='"+document.getElementById("password").value+"' />";
result+="</parameters>";
result+="</request>";
return result;
}
//处理服务器端返回的xml数据
function handleLogonXmlDoc(xmlDoc){
//以下涉及到JavaScript如何读取xml文档对象内容的方法,如不理解请参阅xml和Javacript相关书籍
var nodes=xmlDoc.getElementsByTagName("islogon");
if(nodes[0].getAttribute("value").toString().toLowerCase()=="true"){
varUserName=xmlDoc.getElementsByTagName("UserName")[0].getAttribute("value").toString();
document.getElementById("userInfo").innerText="欢迎您,"+varUserName;
//隐藏登录元素,显示已登录的状态
document.getElementById("logon").style.display="none";
document.getElementById("logoff").style.display="block";
}
}
//logoff的代码
function logoff(handler){
sendRequest(createLogoffXml(handler));
document.getElementById("password").value="";//安全起见,将密码清空
}
//创建将往服务器端发送的xml数据
function createLogoffXml(handler){
var result="";
result+="<request>";
result+="<handler>";
result+=handler;
result+="</handler>";
result+="<parameters>";
result+="<parameter UserName='"+varUserName+"' />";
result+="</parameters>";
result+="</request>";
return result;
}
//处理服务器端返回的xml数据
function handleLogoffXmlDoc(xmlDoc){
//以下涉及到JavaScript如何读取xml文档对象内容的方法,如不理解请参阅xml和Javacript相关书籍
var nodes=xmlDoc.getElementsByTagName("islogon");
if(nodes[0].getAttribute("value").toString().toLowerCase()=="false"){
varUserName=xmlDoc.getElementsByTagName("UserName")[0].getAttribute("value").toString();
//隐藏登录元素,显示已登录的状态
document.getElementById("logon").style.display="block";
document.getElementById("logoff").style.display="none";
}
}
请注意这段代码中的一个框架技巧:函数logon()的参数为handler,在页面中指定了handler的值为“LogonHandler”,也就是最终指定要在服务器端进行处理的类名。函数logon()中调用函数createLogonXml()时,其参数也是handler,就将值“LogonHandler”传入了函数createLogonXml(),而其最终的处理就是将值“LogonHandler”编排进xml数据的<handler>节点。这样,服务器端通过读取xml数据中的<handler>节点,就可以得知应由服务器端的LogonHandler类来处理这段数据了。
3. 我们转入服务器端。客户端的数据是发往服务器端的Handler.aspx。在项目中,加入一个新建的web窗体Handler.aspx。新建此web窗体时,vs2005会生成两个文件:Handler.aspx和Handler.aspx.cs。由于我们的目的是在后台处理客户端发送的数据,不需要直接显示数据,因此将编程环境自动生成的Handler.aspx中的代码只留下:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Handler.aspx.cs" Inherits="Handler" %>
删除了Handler.aspx文件中<html>标记中的代码。当然,保留这些代码也是可以的。
关键是Handler.aspx.cs中的内容。其代码如下:
using System;
using…//为节省篇幅,请参考本文所附示例代码
public partial class Handler : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
//获得客户端发送的xml数据
StreamReader reader = new StreamReader(Request.InputStream);
String result = reader.ReadToEnd();
Response.ContentType = "text/xml";
//调用类AjaxHandler对数据进行处理,返回结果给handledResult
String handledResult = AjaxHandler.HandleAjax(result);
//在此处处理需要在服务器端处理的数据
XmlDocument xmlDocument = new XmlDocument();
try
{
xmlDocument.LoadXml(handledResult);
}
catch (XmlException)
{
return;
}
XmlNodeList serverOperationList = xmlDocument.GetElementsByTagName("serveroperation");
for (int i = 0; i < serverOperationList.Count; i++)
{
if (serverOperationList[i].Attributes["type"].FirstChild.InnerText.Equals("setsession"))
{
Session[serverOperationList[i].Attributes["name"].FirstChild.InnerText] = serverOperationList[i].Attributes["value"].FirstChild.InnerText;
}
}
//将服务器端处理数据删除,避免发往客户端
XmlNodeList serverOperations = xmlDocument.GetElementsByTagName("serveroperations");
for (int i = serverOperations.Count-1; i >= 0; i--)//请注意代码中删除集合多个元素的循环操作应从大到小,否则可能出现不期望的结果
{
serverOperations[i].ParentNode.RemoveChild(serverOperations[i]);//这是删除一个节点数据的技巧
}
Response.Write(xmlDocument.InnerXml);
Response.End();
}
}
请注意,服务器端处理客户端发送来的数据并不是在这里一蹴而就完成的,要调用后台类AjaxHandler的HandleAjax()方法进行分类处理:将各种不同的处理需求发往客户端指定的处理类进行处理。最后将处理后的结果通过Response发往客户端。这也是框架式开发的技巧:不同的功能要由不同的组成部分分别完成,分工要明确。HandlerAjax.aspx处于客户端和服务器端的交界面,其作用就是在双方传递信息。而向后台分发数据的分拣中心,就是AjaxHandler类。
要建立AjaxHandler类,首先应在解决方案资源管理器中,右键点击项目,在弹出的菜单中点击添加ASP.NET文件夹,选择App_Code添加。后台处理类,都添加入App_Code文件夹,这是ASP.NET2.0新增特性,是微软推荐的做法。然后在App_Code文件夹中添加AjaxHandler类,其代码如下:
using System;
using…//为节省篇幅,请参考本文所附示例代码
public class AjaxHandler
{
public static String HandleAjax(String xml)
{
XmlDocument xmlDocument = new XmlDocument();
try
{
xmlDocument.LoadXml(xml);
}
catch (XmlException)
{
return "";
}
//通过反射,由xml中指定的处理类的HandleAjax方法处理数据并返回
object[] parameters ={ xml };
return Type.GetType(xmlDocument.GetElementsByTagName("handler").Item(0).InnerText).GetMethod("HandleAjax").Invoke(null, parameters).ToString();
}
}
反射技术是语言的一种高级特性,跟前文所述的JavaScript的eval()函数颇有些异曲同工之感,都可以实现通过动态生成的字符串来指定程序执行相应的内容。.NET和Java都对反射提供了支持,实现大体相似,只是语句稍有差别。
登录时指定的服务器端处理类为LogonHandler类,因此xmlDocument.GetElementsByTagName("handler").Item(0).InnerText等于LogonHandler,Type.GetType(xmlDocument.GetElementsByTagName("handler").Item(0).InnerText)就在运行时获得了LogonHandler类的信息,调用其GetMethod("HandleAjax")方法,就获得了LogonHandler类的HandAjax()方法的信息。而要调用此方法,就是传入参数数据执行其Invoke()方法。在此反射应用中,Type.GetType(“LogonHandler”).GetMethod("HandleAjax").Invoke(null, parameters)等同于执行LogonHander.HandleAjax(xml),灵活性大为提高,当客户端进行其它操作如指定为登出处理类LogoffHandler时,就可以执行LogoffHandler的HandleAjax()方法,而代码不需做任何改动。
反射技术是框架应用的常用方法,但要注意其执行性能比正常执行方法语句有所下降。如果一个应用对性能要求非常苛刻,那么可考虑采用if/else分支语句来决定执行不同方法,性能要好一些。
我们再来看LogonHandler具体作何处理,而LogoffHandler与LogonHandler大致相同,读者请参考附加示例代码,文中就不做分析。
在App_Code文件夹中添加LogonHandler类,其代码如下:
using System;
using…//为节省篇幅,请参考本文所附示例代码
public class LogonHandler
{
public static String HandleAjax(String xml)
{
//以下涉及到C#如何读取xml文档对象内容的方法,如不理解请参阅C#和Javacript相关书籍
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xml);
XmlNodeList parameterList = xmlDocument.GetElementsByTagName("parameter");
String UserName = parameterList[0].Attributes["UserName"].FirstChild.InnerText;
String Password = parameterList[0].Attributes["Password"].FirstChild.InnerText;
//生成返回的xml数据
StringBuilder result = new StringBuilder();
if (UserName.Equals("user") && Password.Equals("123"))
{
result.AppendLine("<result>");
result.AppendLine("<operations>");
result.AppendLine("<operation type='executeJsText' content='handleLogonXmlDoc(xmlDoc);'/>");
result.AppendLine("</operations>");
result.AppendLine("<serveroperations>");
result.AppendLine("<serveroperation type='setsession' name='UserName' value='" + UserName + "'/>");
result.AppendLine("</serveroperations>");
result.AppendLine("<islogon value='true' />");
result.AppendLine("<UserName value='" + UserName + "' />");
result.AppendLine("</result>");
}
else
{
result.AppendLine("<result>");
result.AppendLine("<operations>");
result.AppendLine("<operation type='alert' content='用户名、密码错误,不能登录'/>");
result.AppendLine("</operations>");
result.AppendLine("<islogon value='false' />");
result.AppendLine("<varUserName value='' />");
result.AppendLine("</result>");
}
return result.ToString();
}
}
原来这段代码逻辑非常简单,就是取出xml数据中的用户名和密码,判断其有效性,然后生成不同的xml数据字符串并返回给调用者。
这里判断用户名、密码是否有效简单的使用了if (UserName.Equals("user") && Password.Equals("123")),在正式应用中往往是在此处插入后台判断代码,如连接数据库进行查询,或连接WebService服务获得结果等等,请读者根据实际开发需要进行修改。
当判断用户名、密码无效,发回的数据包括"<operation type='alert' content='用户名、密码错误,不能登录'/>",数据最终到达客户端由AjaxManage.js中的parseResults()函数处理。在parseResults()函数中,如果处理type为alert,就会将content中的内容直接弹出给用户。
当用户名、密码有效登录成功时,发回数据包括"<operation type='executeJsText' content='handleLogonXmlDoc(xmlDoc);'/>",客户端parseResults()函数中,如果处理type为executeJsTest,就会调用eval()函数执行content中的内容,也就最终在浏览器端执行了预先在AjaxFramework.js中编写好的handleLogonXmlDoc()函数,将xmlDoc作为其参数传入,再由handleLogonXmlDoc()函数对xml数据进行最终分析和处理。
服务器端还应该包括Session、Request、Response等只发生在服务器端的处理操作。本框架以Session操作为例说明了这些操作的实现方式。实际应用中读者可根据需要在与Session操作相应的部位插入Request、Response等处理代码。下面对Session操作进行说明。
登录成功后,应在Session中设置UserName,因此,LogonHandler发回的数据包括"<serveroperation type='setsession' name='UserName' value='" + UserName + "'/>",此数据在返回到Handler.aspx时进行处理。在Handler.aspx.cs中,首先取出serveroperation(服务器处理)数据,当其type为setsession时,以Session[名]=值的方式进行处理。
请注意,需要在服务器端进行处理的数据处理完毕后,规范的做法是将相关数据删除,避免发往客户端,而只留下需要客户端进行处理的数据。因为客户端往往是不安全的,不应显示服务器所做的处理。
4. 客户端由AjaxManage.js的HandleStateChange()函数探测出收到服务器端发回的数据,然后转给parseResults()函数进行处理。而一直说的eval()函数,就是在这里使用。具体代码请参考上文中AjaxManage.js代码。
根据上述4个步骤,就形成了一个客户端发出数据、服务器端进行处理、返回客户端进行处理的完整循环。这样的Ajax处理过程中是无法页面跳转的,如指定服务器处理页面Handler.aspx突然连接不上,客户端就会陷入等待,而不会出现往常所见的“505无法显示网页”错误。当然,处理的最后阶段,可以由服务器发回新的url地址,客户端只要指定document.location=url;就可以进行页面跳转了。
四,Java代码实现
笔者采用的是JDK6+Eclipse 3.1.2+MyEclipse4.1.0开发环境。同样不要求读者是一样的开发环境,只是需要重新引入项目而已。我们用java制作的框架与Visual Studio制作的框架大同小异,因此以下只对某些要点进行着重说明。
通过MyEclipse建立Web Project,还是新建AjaxFramework.htm、AjaxFramework.js、AjaxManage.js三个文件,由于它们与编程语言及开发环境无关,因此只要使用上文给出的几个文件即可。文件中只有一个变化,在AjaxManage.js文件中的sendRequest()函数,发送数据的目的地原本为“Handler.aspx”,其为客户端与服务器端的交界面,而我们在Java应用中采用Servlet作为交界面,因此发送数据的目的地应改为“/AjaxFramework/servlet/Handler”。其中,AjaxFramework是网站项目名称,而“/servlet/Handler”是在web.xml中的<servlet-mapping>节点指定的。实际上,web.xml的很多内容是由MyEclipse自动生成的,我们需要做的,是添加名为Handler的servlet,然后到自动生成的web.xml中找到servlet的地址信息,与项目名相加形成“/AjaxFramework/servlet/Handler”作为发送数据的目的地。
名为Handler的servlet类,其处理流程与ASP.NET框架代码中的Handler.aspx基本一致,只是语法稍有改变,请读者根据本文所附代码阅读。但其中由于Java对汉字编码的处理没有.NET轻松,一定要加入:response.setContentType("text/xml;charset=UTF-8");否则返回到浏览器的汉字,就很可能变为乱码。
对于xml对象和String的相互转换上,Java比较麻烦一些。对于字符串xml,要转换为xml的Document对象,有两种方式:
1. 在类AjaxHandler中的handleRequest()方法中有如下语句:
xmlDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder() .parse(new ByteArrayInputStream(xml.getBytes()));
2. 在类LogonHandler中的HandleAjax()方法中有如下语句:
xmlDoc =DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
这两种方式,程序可读性均不高。其中第1种方式可能出现中文字符转换问题,因此采用第2种方式为好。
五,应注意的问题
1. 应注重对框架的认识
框架实际上也是一种设计模式。一个框架,也要解决一种类型的问题。本文中制作的小型Ajax框架,就是要在网站中加入Ajax模式的应用。那么,对上面的代码看懂了不够,还要知道在这个框架中,面对不同的Ajax应用需求,哪些是需要修改的,而哪些是不变的。
上述用.NET制作的文件,按可变性进行分类,请看下表:
|
l 不变的框架类文件 |
l 要改变的实际应用 |
|
l AjaxManage.js l Handler.aspx l AjaxHandler.cs |
l AjaxFramework.htm l AjaxFramework.js l LogonHandler.cs l LogoffHandler.cs |
表(一)文件可变性分类
本文为了在.NET和Java间移植的方便,对于AjaxFramework.htm采用了Html文件类型,但在实际应用中,Ajax应用往往散布于各个页面,因此对于ASP.NET往往是aspx文件类型,对于Java往往是Jsp类型。
对于各文件在框架中的变与不变,在此举一个ASP.NET的例子说明:
需要用Ajax技术来显示数据,那么在Query.aspx网页中,有一个Query按钮,点击后,应调用query()函数,此函数位于Query.js中,调用Query.js中的createQueryXml()函数,再调用AjaxManage.js中的sendRequest()函数。转到服务器端,会引发Handler.aspx的Page_Load()函数,其中会调用AjaxHandler类的HandleAjax()方法,此方法又会调用QueryHandler类的HandleAjax()方法,得到应返回客户端的数据。数据传到客户端后,会引发AjaxManage.js中的handleStateChange()并调用parseResults()函数。此函数会调用Query.js中的handleQueryXmlDoc()函数,最终完成这一个数据的循环。
在上面写出的各个类中,Query.aspx,Query.js和QueryHandler.cs及其中各函数及方法是根据此应用来新建的,而AjaxManage.js,Handler.aspx,AjaxHandler.cs是不变的。
现实应用中,情况总是错综复杂,上述小型框架往往不能满足一些特定的需要。只要切实掌握了上述框架,就知道应在哪些地方对框架进行修改,以保持框架的健壮性。
2. 命名空间
本文示例代码中,无论是.NET还是Java,都没有使用命名空间。但在实际应用中,读者还是应该将自己的工具类归属为相应的命名空间。值得注意的是,在反射应用传入类名字符串时,一定要带入命名空间,否则只凭类名在运行时是无法找到并调用其方法的。
六,结语
本文用.NET和Java制作Ajax应用的小型框架,而笔者前期进行的一个在线应用项目,正是在此框架基础上进行的开发。希望读者在理解此框架的基础上,对框架的客户端和服务器端都能进行扩展,真正达到实际应用的程度。
本文的示例代码请到http://www.comprg.com.cn下载。
参考文献:
1. [美]Ryan Asleson, Nathaniel T.Schutta 著. 金灵 等译. Ajax基础教程. 北京:人民邮电出版社,2006
2. [美]David Flanagan著. 张铭泽等译. JavaScript权威指南(第四版). 北京:机械工业出版社,2006

浙公网安备 33010602011771号