1.6 最简单的Ajax应用程序
让我们通过实现一个简单而又实用的Ajax功能来熟悉Ajax应用程序的开发。本书虽然侧重介绍已经良好封装的ASP.NET AJAX框架,但适当了解一下Ajax的实现原理仍然是非常必要的,而且说不定你的项目马上就会用到这个功能呢!
我们都知道,在通常的用户注册表单中,用户一般都可以选择并输入一个用户名以备登录时所需。这样,这个作为ID的用户名就需要保证在系统中是唯一的。因为现有用户的ID信息只在服务器的数据库中才有保留,所以当用户填写注册表单时,客户端无法知道他输入的用户名是否已经被占用。
为了解决这个问题,很多网站中常见的做法是在用户名旁边加上一个检查用户名是否可用的按钮Check User Name(参见图1-11)。对于程序开发者来说,这样的设计真是太恰当不过了,实现起来也非常简单。例如,若是使用ASP.NET开发,只需要在Visual Studio设计器中双击这个按钮,然后使用IDE自动生成的代码查询一下数据库即可。

图1-11 让用户迷惑的Check User Name按钮
但是对于那些不懂技术的用户而言,这往往会让他们迷惑:我已经输入了用户名,难道程序不能自动检查一下吗?还有,这个页面上有两个按钮(Check User Name和Submit),我应该点击哪一个?或者要按照什么顺序点击呢?
噢,这真是个失败的设计。但是开发人员也有他的苦衷:谁让HTTP协议天生就是这个样子的呢?
现在,让我们借助Ajax的强大功能,在用户填写表单的其他部分时让页面自动去服务器端检测该用户名是否可用。也就是说,彻底抛弃那个让用户迷惑的Check User Name按钮!
完成后的程序界面将如图1-12所示,某个用户已经输入了他期望的用户名,并正在填写密码部分。而不巧的是经过服务器端验证,该用户名已经有人使用了,因此用户名文本框旁边将显示一条红色的错误信息,且提交按钮也会被禁用。若用户名可用,则什么都不显示。你可能会问:难道没有任何提示吗?正是这样。我们提供给用户的信息应该用于引起用户的注意并引导他进行某些操作,而不应该是一条毫无指导意义的祝贺信息,例如“恭喜,你可以使用这个用户名”,干扰用户的其他操作。

图1-12 使用Ajax在不知不觉中为用户检查输入
让我们看一下定义这个页面的HTML标记代码。限于篇幅,这里只列出了表单部分代码,可以直接运行的完整代码请在图灵网站(http://www.turingbook.com/)下载。
代码如下:
<table id="registerForm">
<tr>
<td class="title">
User Name
</td>
<td>
<input id="tbUserName" type="text" onblur="checkUserName()" />
<span id="checkResult"></span>
</td>
</tr>
<tr>
<td class="title">
Password
</td>
<td>
<input id="tbPassword" type="password" />
</td>
</tr>
<tr>
<td rowspan="2">
<input id="btnSubmit" type="submit" value="Submit" />
</td>
</tr>
</table>
上述代码定义了一个表格,用来格式化表单的内容。其中有三个<input>标签,其id分别为tbUserName、tbPassword以及btnSubmit。前两个<input>作为输入框出现(通过将type属性设置为text),分别用来让用户输入用户名和密码。第三个<input>作为提交按钮(通过将type属性设置为submit),用户可以点击该按钮提交该注册表单。tbUserName还定义了DHTML事件on blur的事件处理函数(使用onblur="checkUserName()"),这样当该文本框失去输入焦点时,checkUserName()函数将被调用。你还会注意到tbUserName后面定义了一个id为checkResult的<span>,它用于显示验证失败信息。当然,上述页面只是为了演示Ajax的功能,所以其他部分均从简处理,真正的用户注册表单一般要远比这个示例中所示出的复杂。
该页面中还引用了一个CSS文件,代码如下:
*
{
margin: 0;
padding: 0;
font-family: Tahoma;
font-size: 0.96em;
}
#registerForm
{
width: 450px;
margin: 20px;
border: 1px solid #000;
}
#registerForm .title
{
width: 70px;
text-align: right;
padding: 7px;
}
#registerForm input
{
border: 1px solid #000;
}
#checkResult
{
color: #f00;
}
从中可以看到,页面上的错误信息(通过#checkResult定义)将以红色显示。
我们接下来看一下页面中的JavaScript脚本。首先声明一个全局变量,用来保存XMLHttp- Request对象的引用。注意,这里并没有对其进行初始化:
var xmlHttp = false;
接下来是当用户名文本框失去输入焦点时,响应这个事件的checkUserName()函数,声明如下:
function checkUserName()
{
}
该函数比较复杂,让我们依次查看实现代码。首先是检查用户名文本框中是否有输入;如果没有输入,则无需到服务器端进行下一步检查:
// 检查tbUserName中是否有输入。
var tbUserName = document.getElementById('tbUserName');
if (tbUserName.value == "")
return;
然后是创建XMLHttpRequest对象部分(因为目前XMLHttpRequest对象在各个浏览器中的实现机制不甚相同,所以我们需要综合考虑每种浏览器。限于篇幅,这里不对其仔细分析。若希望更深入了解,可以参考专门讲解Ajax的书籍):
// 创建XMLHttpRequest对象。
try
{
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e)
{
try
{
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e2)
{
xmlHttp = false;
}
}
if (!xmlHttp && typeof XMLHttpRequest != 'undefined') {
xmlHttp = new XMLHttpRequest();
}
接下来构造了一个该XMLHttpRequest对象将请求的服务器端CGI的URL的字符串,这里服务器端CGI采用ASP.NET编写,名为CheckUserNameService.aspx,该CGI接受一个参数UserName,我们将其值设定为在用户名文本框中输入的文字:
// 创建服务器端CGI的URL。
var url = "CheckUserNameService.aspx?UserName=" + tbUserName.value;
然后调用XMLHttpRequest对象的open()方法,建立与服务器的连接:
// 连接到服务器。
xmlHttp.open("GET", url, true);
指定该异步调用的回调函数为callBack_CheckUserName():
// 设置回调函数。
xmlHttp.onreadystatechange = callBack_CheckUserName;
最后发送该请求:
// 发送请求。
xmlHttp.send(null);
当该请求到达服务器时,服务器将检查该用户名是否已经被使用,下面是服务器端Check- UserNameService.aspx文件的代码,使用C#编写:
protected void Page_Load(object sender, EventArgs e)
{
// 得到用户希望选择的用户名。
string candidateUserName = Request["UserName"];
// 验证是否被注册(这里仅为模拟)。
bool isValid = false;
if (candidateUserName.ToLower() != "dflying")
{
isValid = true;
}
// 发送验证结果。
Response.Clear();
Response.Write(isValid ? "true" : "false");
Response.Flush();
}
可以看到,在验证时,只有用户期望的用户名Dflying已经被使用时返回false,而当用户名可用时则返回true。在实际程序中,这一步往往需要通过查询数据库后才能得到结果。
当客户端XMLHttpRequest对象收到服务器的响应时,callBack_CheckUserName()回调函数将被调用。下面是该函数的代码:
function callBack_CheckUserName()
{
if (xmlHttp.readyState == 4)
{
var isValid = xmlHttp.responseText;
var checkResult = document.getElementById("checkResult");
checkResult.innerHTML = (isValid == "true") ? "" : "Please choose another name.";
}
}
在上面的代码中,我们首先检查XMLHttpRequest对象的readyState属性值是否为4,4代表服务器端执行完毕并已经成功返回。然后通过responseText属性得到服务器端实际返回的文本(字符串"true"或"false"),并根据这个值决定checkResult Label的显示内容。若返回值为false,则表示该用户名已经被其他人使用,这样checkResult Label将显示"Please choose another name."。
这样即完成了本程序。可以看到,通过使用Ajax技术,我们能够以异步的方式在用户不查觉的情况下与服务器端通信,从而带来流畅的用户体验。限于篇幅,上文中并没有关于XMLHttp- Request对象属性、方法等的详细介绍;若对其感兴趣,请参考其他资料。







