使用 JavaScript 和 Ajax 发出异步请求
本章节对JavaWeb——AJAX(1)中,XMLHttpRequest对象进行详解。从创建,请求到响应的整个过程。
1、XMLHttpRequest对象简介
XMLHttpRequest对象是一个JavaScript对象,下面列出了用于该对象的几个方法和属性:
- open(): 建立到服务器的请求
- send(): 想服务器发送请求
- abort(): 退出当前请求
- readyState: 提供当前HTML的就绪状态
- responseText: 服务器返回的请求响应文本
2、创建XMLHttpRequest对象
上一节中给出了支持多种浏览器的创建XMLHttpRequest对象的方式,代码如下所示:
/* Create a new XMLHttpRequest object to talk to the Web server */
var xmlHttp = false;
/*@cc_on @*/
/*@if (@_jscript_version >= 5)
try {
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e2) {
xmlHttp = false;
}
}
@end @*/
if (!xmlHttp && typeof XMLHttpRequest != 'undefined') {
xmlHttp = new XMLHttpRequest();
}
该段代码的核心:
- 建立一个变量xmlHttp来引用即将创建的XMLHttpRequest对象。
- 尝试在Microsoft浏览器中创建该对象(两种方式)。
- 如果仍然没有建立xmlHttp,则以非Microsoft的方式创建该对象。
得到请求对象之后就可以进入请求/响应循环了。记住,XMLHttpRequest 惟一的目的是让您发送请求和接收响应。其他一切都是 JavaScript、CSS 或页面中其他代码的工作:改变用户界面、切换图像、解释服务器返回的数据。准备好 XMLHttpRequest 之后,就可以向服务器发送请求了。
此外需要注意的是,这些代码是写在<script language="javascript" type="text/javascript">
中,即直接潜逃在script标记中。这种不放在方法或函数体中的JavaScript代码称为静态JavaScript。这是多数AJAX程序员创建XMLHttpRequest对象的一般方法。
当然我们可以把这部分代码放到函数中,例如:
<script language="javascript" type="text/javascript">
var request;
function createRequest() {
try {
request = new XMLHttpRequest();
} catch (trymicrosoft) {
try {
request = new ActiveXObject("Msxml2.XMLHTTP");
} catch (othermicrosoft) {
try {
request = new ActiveXObject("Microsoft.XMLHTTP");
} catch (failed) {
request = false;
}
}
}
if (!request)
alert("Error initializing XMLHttpRequest!");
}
</script>
这样编写代码,我们还需要在处理AJAX之前调用这部分代码,因此还需要以下代码:
function getCustomerInfo() {
createRequest();
// Do something with the request variable
}
动态JavaScript方法的弊端是,推迟了错误通知。因为静态JavaScript保证了这些代码在用户能够与页面进行交互之前运行,而动态JavaScript,等待用户在输入文本时激活了某些ajax代码,这时如果出现不能创建XMLHttpRequest对象的问题,警告用户不能使用该应用程序,就太晚了。
因此,我们建议使用静态JavaScript的方式来实例化一个XMLHttpRequest对象。
3、 请求与响应的详细讲解
function callServer() {
var city = document.getElementById("city").value;
var state = document.getElementById("state").value;
if ((city == null) || (city == "")) return;
if ((state == null) || (state == "")) return;
var url = "/scripts/getZipCode.php?city=" + escape(city) + "&state=" + escape(state);
xmlHttp.open("GET", url, true);
xmlHttp.onreadystatechange = updatePage;
xmlHttp.send(null);
}
function updatePage() {
if (xmlHttp.readyState == 4) {
var response = xmlHttp.responseText;
document.getElementById("zipCode").value = response;
}
}
上一节给出了请求和响应的代码,现在详细地解释一下。
3.1 建立URL
var city = document.getElementById("city").value;
这一句将表单中id为city的属性值赋给了city对象。
escape()方法用于转义不能用明文正确发送的任何字符,比如空格和中文字符。
3.2 打开请求
xmlHttp.open()
方法用来配置请求。该方法有五个参数:
- request-type:发送请求的类型:GET/POST/HEAD
- rul:要链接的URL
- asynch:如果使用异步连接则为true
- username:身份验证的用户名。
- password:身份验证的口令。
3.3 发送请求
xmlHttp.send()
方法用来发送请求,send只有一个参数,即要发送的内容。在GET请求中,用URL发送数据要容易,如果需要发送安全信息或者XML,则要考虑用send()传递数据。
3.4 指定回调方法
JavaScript 是一种弱类型的语言,可以用变量引用任何东西。因此如果声明了一个函数 updatePage(),JavaScript 也将该函数名看作是一个变量。换句话说,可用变量名 updatePage 在代码中引用函数。
因此,使用xmlHttp.onreadystatechange = updatePage;
设置回调方法。注意,这一行代码要放在send之前设置。发送请求之前不许设置这一属性,这样服务器才能在回答完请求之后查看及触发该函数。
3.5 处理服务器响应的时机
上一节中讲到,处理响应需要注意的两点:
- 什么也不用做,等待xmlHttp.readyState属性值为4。
- 服务器把响应填充到xmlHttp.responseText属性中,可以从中获得。
xmlHttp.onreadystatechange = updatePage
已经指定了服务器完成请求后运行的函数名,在该函数中,需要判断的第一个信息是HTTP的就绪状态——readyState属性。
HTTP就绪状态有一下五种:
- 0:请求没有发出(在调用 open() 之前)。
- 1:请求已经建立但还没有发出(调用 send() 之前)。
- 2:请求已经发出正在处理之中(这里通常可以从响应得到内容头部)。
- 3:请求已经处理,响应中通常有部分数据可用,但是服务器还没有完成响应。
- 4:响应已完成,可以访问服务器响应并使用它。
对于AJAX编程,需要直接处理的唯一状态是就绪状态4,表示服务器响应已经完成了。
事实上,xmlHttp.onreadystatechange = updatePage
这句代码,表示xmlHttp对象的readyState对象改变数值时,就会调用updatePage函数。可以在进入updatePate函数时提示当前的xmlHttp对象的readystate属性的值,来证明这一观点。
function updatePage() {
// Output the current ready state
alert("updatePage() called with ready state of " + request.readyState);
}
在浏览器中可以看到这样的提示:
除此之外,还需要考虑HTTP的状态码 ,我们期待的HTTP状态码是200,当然也404、403、401也很常见,我们可以加入一些这样的代码:
if(xmlHttp.readyState == 4){
if(xmlHttp.status == 200){
alert("Server is done!");
}
}
3.6 读取响应文本
返回的数据保存在XMLHttpRequest对象的responseText属性中。在下面这个例子中,服务器返回客户的上一个订单和客户地址,中间用“|”管道符分开。
function updatePage() {
if (request.readyState == 4) {
if (request.status == 200) {
var response = request.responseText.split("|");
document.getElementById("order").value = response[0];
document.getElementById("address").innerHTML =
response[1].replace(/\n/g, "");
} else
alert("status is " + request.status);
}
}
3.7 错误处理
重定向和重新路由
重定向问题是AJAX并不需要关心的问题,原因是:首先,AJAX应用程序通常都是为一个特定的服务器端脚本、servlet或应用程序而编写的;其次,AJAX应用程序和请求都是封装在沙盒中的。这就意味着提供生成Ajax请求的web页面的域必须是对这些请求都响应的域。
在HTTP状态代码中,重定向涉及到300系列的状态代码。包括:
- 301:永久移动
- 302:找到(请求被重新定向到另一个URL/URI上)
- 305 :使用代理(请求必须使用一个代理来访问所请求的资源)
错误——边界情况和困难情况
错误情况出现的比率非常小,但是应用程序正常工作100次都会被忘记,一次出错就会被记住。
300系列的状态代码可以很大程度上被忽略,所需要担心的唯一一组代码就是400系列代码。就绪状态为2,3,状态代码为403,405之类的情况,我们称之为边界情况。这些边界情况是用户所碰到的问题的80%. 尽管这些问题在使用应用程序的过程中可能出现的概率只有1%. 因此,我们可以在updatePage函数中使用小的alert提醒,对错误进行处理。
function updatePage() {
if (request.readyState == 4) {
if (request.status == 200) {
var response = request.responseText.split("|");
document.getElementById("order").value = response[0];
document.getElementById("address").innerHTML =
response[1].replace(/\n/g, "<br />");
} else if (request.status == 404) {
alert ("Requested URL is not found.");
} else if (request.status == 403) {
alert("Access denied.");
} else
alert("status is " + request.status);
}
}
使用HEAD请求
当不确定有关脚本或服务器端组件的情况下,可以使用HEAD请求,而不是GET或者POST,不需要对响应数据真正进行处理,也不需要大量的带宽来发送响应。HEAD请求返回的是响应头信息,可以查看响应内容长度和内容类型等属性信息,基于这些可以判断响应的信息是否需要再次请求。
HEAD请求:request.open("HEAD",url,ture);
查看响应长度:request.getResponseHeader("Content-Length");
获取内容类型:request.getResponseHeader("Content-Type");