xiaoyaoyou - 2009-3-30 20:08:48
Ajax 世界中的请求/响应
现在我们介绍了 Ajax,对 XMLHttpRequest 对象以及如何创建它也有了基本的了解。如果阅读得很仔细,您可能已经知道与服务器上的 Web 应用程序打交道的是 JavaScript 技术,而不是直接提交给那个应用程序的 HTML 表单。
还缺少什么呢?到底如何使用 XMLHttpRequest。因为这段代码非常重要,您编写的每个 Ajax 应用程序都要以某种形式使用它,先看看 Ajax 的基本请求/响应模型是什么样吧。
发出请求
您已经有了一个崭新的 XMLHttpRequest 对象,现在让它干点活儿吧。首先需要一个 Web 页面能够调用的 JavaScript 方法(比如当用户输入文本或者从菜单中选择一项时)。接下来就是在所有 Ajax 应用程序中基本都雷同的流程:
从 Web 表单中获取需要的数据。
建立要连接的 URL。
打开到服务器的连接。
设置服务器在完成后要运行的函数。
发送请求。
清单 5 中的示例 Ajax 方法就是按照这个顺序组织的:
清单 5. 发出 Ajax 请求
function callServer() {
// Get the city and state from the web form
var city = document.getElementById("city").value;
var state = document.getElementById("state").value;
// Only go on if there are values for both fields
if ((city == null) || (city == "")) return;
if ((state == null) || (state == "")) return;
// Build the URL to connect to
var url = "/scripts/getZipCode.php?city=" + escape(city) + "&state=" + escape(state);
// Open a connection to the server
xmlHttp.open("GET", url, true);
// Setup a function for the server to run when it's done
xmlHttp.onreadystatechange = updatePage;
// Send the request
xmlHttp.send(null);
}
其中大部分代码意义都很明确。开始的代码使用基本 JavaScript 代码获取几个表单字段的值。然后设置一个 PHP 脚本作为链接的目标。要注意脚本 URL 的指定方式,city 和 state(来自表单)使用简单的 GET 参数附加在 URL 之后。
然后打开一个连接,这是您第一次看到使用 XMLHttpRequest。其中指定了连接方法(GET)和要连接的 URL。最后一个参数如果设为 true,那么将请求一个异步连接(这就是 Ajax 的由来)。如果使用 false,那么代码发出请求后将等待服务器返回的响应。如果设为 true,当服务器在后台处理请求的时候用户仍然可以使用表单(甚至调用其他 JavaScript 方法)。
open() 是打开吗?
Internet 开发人员对 open() 方法到底做什么没有达成一致。但它实际上并不是 打开一个请求。如果监控 XHTML/Ajax 页面及其连接脚本之间的网络和数据传递,当调用 open() 方法时将看不到任何通信。不清楚为何选用了这个名字,但显然不是一个好的选择。
有了要连接的 URL 后就可以配置请求了。可以用 XMLHttpRequest 对象的 open() 方法来完成。该方法有五个参数:
request-type:发送请求的类型。典型的值是 GET 或 POST,但也可以发送 HEAD 请求。
url:要连接的 URL。
asynch:如果希望使用异步连接则为 true,否则为 false。该参数是可选的,默认为 true。
username:如果需要身份验证,则可以在此指定用户名。该可选参数没有默认值。
password:如果需要身份验证,则可以在此指定口令。该可选参数没有默认值。
通常使用其中的前三个参数。事实上,即使需要异步连接,也应该指定第三个参数为 “true”。这是默认值,但坚持明确指定请求是异步的还是同步的更容易理解。
xmlHttp(要记住,这是 XMLHttpRequest 对象实例)的 onreadystatechange 属性可以告诉服务器在运行完成 后(可能要用五分钟或者五个小时)做什么。因为代码没有等待服务器,必须让服务器知道怎么做以便您能作出响应。在这个示例中,如果服务器处理完了请求,一个特殊的名为 updatePage() 的方法将被触发。
最后,使用值 null 调用 send()。因为已经在请求 URL 中添加了要发送给服务器的数据(city 和 state),所以请求中不需要发送任何数据。这样就发出了请求,服务器按照您的要求工作。
如果没有发现任何新鲜的东西,您应该体会到这是多么简单明了!除了牢牢记住 Ajax 的异步特性外,这些内容都相当简单。应该感激 Ajax 使您能够专心编写漂亮的应用程序和界面,而不用担心复杂的 HTTP 请求/响应代码。
清单 5 中的代码说明了 Ajax 的易用性。数据是简单的文本,可以作为请求 URL 的一部分。用 GET 而不是更复杂的 POST 发送请求。没有 XML 和要添加的内容头部,请求体中没有要发送的数据;换句话说,这就是 Ajax 的乌托邦。
不用担心,随着本系列文章的展开,事情会变得越来越复杂。您将看到如何发送 POST 请求、如何设置请求头部和内容类型、如何在消息中编码 XML、如何增加请求的安全性,可以做的工作还有很多!暂时先不用管那些难点,掌握好基本的东西就行了,很快我们就会建立一整套的 Ajax 工具库。
处理响应
HTTP 就绪状态
前面提到,服务器在完成请求之后会在 XMLHttpRequest 的 onreadystatechange 属性中查找要调用的方法。这是真的,但还不完整。事实上,每当 HTTP 就绪状态改变时它都会调用该方法。这意味着什么呢?首先必须理解 HTTP 就绪状态。
HTTP 就绪状态表示请求的状态或情形。它用于确定该请求是否已经开始、是否得到了响应或者请求/响应模型是否已经完成。它还可以帮助确定读取服务器提供的响应文本或数据是否安全。在 Ajax 应用程序中需要了解五种就绪状态:
0:请求没有发出(在调用 open() 之前)。
1:请求已经建立但还没有发出(调用 send() 之前)。
2:请求已经发出正在处理之中(这里通常可以从响应得到内容头部)。
3:请求已经处理,响应中通常有部分数据可用,但是服务器还没有完成响应。
4:响应已完成,可以访问服务器响应并使用它。
与大多数跨浏览器问题一样,这些就绪状态的使用也不尽一致。您也许期望任务就绪状态从 0 到 1、2、3 再到 4,但实际上很少是这种情况。一些浏览器从不报告 0 或 1 而直接从 2 开始,然后是 3 和 4。其他浏览器则报告所有的状态。还有一些则多次报告就绪状态
现在要面对服务器的响应了。现在只要知道两点:
什么也不要做,直到 xmlHttp.readyState 属性的值等于 4。
服务器将把响应填充到 xmlHttp.responseText 属性中。
其中的第一点,即就绪状态,将在下一篇文章中详细讨论,您将进一步了解 HTTP 请求的阶段,可能比您设想的还多。现在只要检查一个特定的值(4)就可以了(下一期文章中还有更多的值要介绍)。第二点,使用 xmlHttp.responseText 属性获得服务器的响应,这很简单。清单 6 中的示例方法可供服务器根据 清单 5 中发送的数据调用。
清单 6. 处理服务器响应
function updatePage() {
if (xmlHttp.readyState == 4) {
var response = xmlHttp.responseText;
document.getElementById("zipCode").value = response;
}
}
HTTP 状态码
虽然 清单 6 中的代码看起来似乎不错,但是还有一个问题 —— 如果服务器响应请求并完成了处理但是报告了一个错误怎么办?要知道,服务器端代码应该明白它是由 Ajax、JSP、普通 HTML 表单或其他类型的代码调用的,但只能使用传统的 Web 专用方法报告信息。而在 Web 世界中,HTTP 代码可以处理请求中可能发生的各种问题。
比方说,您肯定遇到过输入了错误的 URL 请求而得到 404 错误码的情形,它表示该页面不存在。这仅仅是 HTTP 请求能够收到的众多错误码中的一种(完整的状态码列表请参阅 参考资料 中的链接)。表示所访问数据受到保护或者禁止访问的 403 和 401 也很常见。无论哪种情况,这些错误码都是从完成的响应 得到的。换句话说,服务器履行了请求(即 HTTP 就绪状态是 4)但是没有返回客户机预期的数据。
因此除了就绪状态外,还需要检查 HTTP 状态。我们期望的状态码是 200,它表示一切顺利。如果就绪状态是 4 而且状态码是 200,就可以处理服务器的数据了,而且这些数据应该就是要求的数据(而不是错误或者其他有问题的信息)。因此还要在回调方法中增加状态检查,如 清单 14 所示。
清单 14. 检查 HTTP 状态码
function updatePage() {
if (request.readyState == 4)
if (request.status == 200)
alert("Server is done!");
}
为了增加更健壮的错误处理并尽量避免过于复杂,可以增加一两个状态码检查,请看一看 清单 15 中修改后的 updatePage() 版本。
清单 15. 增加一点错误检查
function updatePage() {
if (request.readyState == 4)
if (request.status == 200)
alert("Server is done!");
else if (request.status == 404)
alert("Request URL does not exist");
else
alert("Error: status code is " + request.status);
}
这些代码同样既不难也不复杂。它等待服务器调用,如果是就绪状态,则使用服务器返回的值(这里是用户输入的城市和州的 ZIP 编码)设置另一个表单字段的值。于是包含 ZIP 编码的 zipCode 字段突然出现了,而用户没有按任何按钮!这就是前面所说的桌面应用程序的感觉。快速响应、动态感受等等,这些都只因为有了小小的一段 Ajax 代码。
细心的读者可能注意到 zipCode 是一个普通的文本字段。一旦服务器返回 ZIP 编码,updatePage() 方法就用城市/州的 ZIP 编码设置那个字段的值,用户就可以改写该值。这样做有两个原因:保持例子简单,说明有时候可能希望 用户能够修改服务器返回的数据。要记住这两点,它们对于好的用户界面设计来说很重要。