大纲:
ajax
创建XmlhttpRequest对象
发出ajax请求:ajax同步和异步的区别
处理服务器响应:1.浏览器状态的不一致性;2.获取安全数据
深入了解HTTP状态码:1.检查有效状态吗.2 重定向和重新路由.
有用的HEAD请求
部分ajax伪代码
老技术新应用
在谈到 Ajax 时,实际上涉及到多种技术,要灵活地运用它必须深入了解这些不同的技术(本系列的头几篇文章将分别讨论这些技术)。好消息是您可能已经非常熟悉其中的大部 分技术,更好的是这些技术都很容易学习,并不像完整的编程语言(如 Java 或 Ruby)那样困难。
Ajax的定义:顺便说一下,Ajax 是 Asynchronous JavaScript and XML(以及 DHTML 等)的缩写。这个短语是 Adaptive Path 的 Jesse James Garrett 发明的(请参阅 参考资料),按照 Jesse 的解释,这不是 个首字母缩写词。
下面是 Ajax 应用程序所用到的基本技术:
- HTML 用于建立 Web 表单并确定应用程序其他部分使用的字段。
- JavaScript 代码是运行 Ajax 应用程序的核心代码,帮助改进与服务器应用程序的通信。
- DHTML 或 Dynamic HTML,用于动态更新表单。我们将使用 div、span 和其他动态 HTML 元素来标记 HTML。
- 文档对象模型 DOM 用于(通过 JavaScript 代码)处理 HTML 结构和(某些情况下)服务器返回的 XML。
创建XmlhttpRequest对象:
使用浏览器: Microsoft 浏览器 Internet Explorer 使用 MSXML 解析器处理 XML(可以通过 参考资料 进一步了解 MSXML)。因此如果编写的 Ajax 应用程序要和 Internet Explorer 打交道,那么必须用一种特殊的方式创建对象。
但并不是这么简单。根据 Internet Explorer 中安装的 JavaScript 技术版本不同,MSXML 实际上有两种不同的版本,因此必须对这两种情况分别编写代码。请参阅 清单 3,其中的代码在 Microsoft 浏览器上创建了一个 XMLHttpRequest
。
var xmlHttp = false; try { xmlHttp = new ActiveXObject("Msxml2.XMLHTTP"); // IE6 + } catch (e) { try { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); // IE5.5+, 其实用这一个也行; } catch (e2) { xmlHttp = false; } } |
b )处理 Mozilla 和非 Microsoft 浏览器
xmlHttp = new XMLHttpRequest();
结合起来:
var xmlHttp = false;
try {
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
try {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
xmlHttp = false;
}
}
if (!xmlHttp && typeof XMLHttpRequest != 'undefined') {
xmlHttp = new XMLHttpRequest();
}
发出ajax请求
Function callServer(){
Var url = "/scripts/getZipCode.php?city=" + escape(city) + "&state=" + escape(state);
xmlHttp.open("GET", url, true);
open参数介绍:
有了要连接的 URL 后就可以配置请求了。可以用 XMLHttpRequest 对象的 open() 方法来完成。该方法有五个参数:
· request-type:发送请求的类型。典型的值是 GET 或 POST,但也可以发送 HEAD 请求。
· url:要连接的 URL。
· asynch:如果希望使用异步连接则为 true,否则为 false。该参数是可选的,默认为 true。
· username:如果需要身份验证,则可以在此指定用户名。该可选参数没有默认值。
· password:如果需要身份验证,则可以在此指定口令。该可选参数没有默认值。
同步:提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事
异步: 请求通过事件触发->服务器处理(这是浏览器仍然可以作其他事情)->处理完毕
同步不需要onreadystatechange事件处理函数。而异步则需要onreadystatechange事件处理
// Setup a function for the server to run when it's done
xmlHttp.onreadystatechange = updatePage;
// 默认情况下,服务器对POST请求和提交Web表单的请求并不会一视同仁。因此,服务器必须有程序来读取发送过来的原始数据,并从中解析有用的部分。不过我们可以使用xhr来模仿表单提交;首先将Content-Type头信息设置为application/x-www-form-urlencoded,也就是表单提交时的内容类型,其次是创建适当格式的一个字符串;
在使用post请求的时候,设置;
// xmlHttp.setRequestHeaer(‘Content-Type’,’application/x-www-form-urlencoded’);
// Send the request
xmlHttp.send(null);
}
处理服务器响应
function updatePage() {
if (xmlHttp.readyState == 4) {
var response = xmlHttp.responseText;
document.getElementById("zipCode").value = response;
}
}
这显然是就绪状态最常见(也是最简单)的用法。正如您从数字 "4" 中可以看出的一样,还有其他几个就绪状态(您在上一篇文章中也看到过这个清单 —— 请参见 参考资料):
· 0:请求未初始化(还没有调用 open())。
· 1:请求已经建立,但是还没有发送(还没有调用 send())。
· 2:请求已发送,正在处理中(通常现在可以从响应中获取内容头)。
· 3:请求在处理中;通常响应中已有部分数据可用了,但是服务器还没有完成响应的生成。
· 4:响应已完成;您可以获取并使用服务器的响应了。
浏览器的不一致性
在对这个过程有一个基本的了解之后,请试着从几个不同的浏览器中访问您的页面。您应该会注意到各个浏览器如何处理这些就绪状态并不一致。例如,在 Firefox 1.5 中,您会看到以下就绪状态:
· 1
· 2
· 3
· 4
这并不奇怪,因为每个请求状态都在这里表示出来了。然而,如果您使用 Safari 来访问相同的应用程序,就应该看到 —— 或者看不到 —— 一些有趣的事情。下面是在 Safari 2.0.1 中看到的状态:
· 2
· 3
· 4
Safari 实际上把第一个就绪状态给丢弃了,也并没有什么明显的原因说明为什么要这样做;不过这就是 Safari 的工作方式。这还说明了一个重要的问题:尽管在使用服务器上的数据之前确保请求的状态为 4 是一个好主意,但是依赖于每个过渡期就绪状态编写的代码的确会在不同的浏览器上得到不同的结果。
例如,在使用 Opera 8.5 时,所显示的就绪状态情况就更加糟糕了:
· 3
· 4
最后,Internet Explorer 会显示如下状态:
· 1
· 2
· 3
· 4
如果您碰到请求方面的问题,这就是用来发现问题的 首要之处。最好的方式是在 Internet Explorer 和 Firefox 都进行一下测试 —— 您会看到所有这 4 种状态,并可以检查请求的每个状态所处的情况。
获取安全数据
所有的文档和规范都强调,只有在就绪状态为 4 时数据才可以安全使用。相信我,当就绪状态为 3 时,您很少能找到无法从 responseText
属性获取数据的情况。然而,在应用程序中将自己的逻辑依赖于就绪状态 3 可不是什么好主意 —— 一旦您编写了依赖于就绪状态 3 的完整数据的的代码,几乎就要自己来负责当时的数据不完整问题了。
比较好的做法是向用户提供一些反馈,说明在处于就绪状态 3 时,很快就会有响应了。尽管使用 alert()
之类的函数显然不是什么好主意 —— 使用 Ajax 然后使用一个警告对话框来阻塞用户显然是错误的 —— 不过您可以在就绪状态发生变化时更新表单或页面中的域。例如,对于就绪状态 1 来说要将进度指示器的宽度设置为 25%,对于就绪状态 2 来说要将进度指示器的宽度设置为 50%,对于就绪状态 3 来说要将进度指示器的宽度设置为 75%,当就绪状态为 4 时将进度指示器的宽度设置为 100%(完成)。
当然,正如您已经看到的一样,这种方法非常聪明,但它是依赖于浏览器的。在 Opera 上,您永远都不会看到前两个就绪状态,而在 Safari 上则没有第一个(1)。由于这个原因,我将这段代码留作练习,而没有在本文中包括进来。
深入了解HTTP状态代码
有了就绪状态和您在 Ajax 编程技术中学习到的服务器的响应,您就可以为 Ajax 应用程序添加另外一级复杂性了 —— 这要使用 HTTP 状态代码。这些代码对于 Ajax 来说并没有什么新鲜。从 Web 出现以来,它们就已经存在了。在 Web 浏览器中您可能已经看到过几个状态代码:
- 401:未经授权
- 403:禁止
- 404:没找到
更多的状态吗:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
200:一切正常
在很多 Ajax 应用程序中,您将看到一个回调函数,它负责检查就绪状态,然后继续利用从服务器响应中返回的数据,如 下所示
function updatePage() {
if (request.readyState == 4) {
var response = request.responseText.split("|");
document.getElementById("order").value = response[0];
document.getElementById("address").innerHTML =
response[1].replace(//n/g, "<br />");
}
}
这对于 Ajax 编程来说证明是一种短视而错误的方法。如果脚本需要认证,而请求却没有提供有效的证书,那么服务器就会返回诸如 403 或 401 之类的错误代码。然而,由于服务器对请求进行了应答,因此就绪状态就被设置为 4(即使应答并不是请求所期望的也是如此)。最终,用户没有获得有效数据,当 JavaScript 试图使用不存在的服务器数据时就可能会出现严重的错误。
:
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{
alert("status is " + request.status);
}
}
}
通过添加这几行代码,您就可以确认是否存在问题,用户会看到一个有用的错误消息,而不仅仅是看到一个由断章取义的数据所构成的页面,而没有任何解释。
- 301:永久移动
- 302:找到(请求被重新定向到另外一个 URL/URI 上)
- 305:使用代理(请求必须使用一个代理来访问所请求的资源)
Ajax 程序员可能并不太关心有关重定向的问题,这是由于两方面的原因:
- 首先,Ajax 应用程序通常都是为一个特定的服务器端脚本、servlet 或应用程序而编写的。对于那些您看不到就消失了的组件来说,Ajax 程序员就不太清楚了。因此有时您会知道资源已经移动了(因为您移动了它,或者通过某种手段移动了它),接下来要修改请求中的 URL,并且不会再碰到这种结果了。
- 更为重要的一个原因是:Ajax 应用程序和请求都是封装在沙盒中的。这就意味着提供生成 Ajax 请求的 Web 页面的域必须是对这些请求进行响应的域。因此 ebay.com 所提供的 Web 页面就不能对一个在 amazon.com 上运行的脚本生成一个 Ajax 风格的请求;在 ibm.com 上的 Ajax 应用程序也无法对在 netbeans.org 上运行的 servlets 发出请求。
结果是您的请求无法重定向到其他服务器上,而不会产生安全性错误。在这些情况中,您根本就不会得到状态代码。通常在调试控制台中都会产生一个 JavaScript 错误。因此,在对状态代码进行充分的考虑之后,您就可以完全忽略重定向代码的问题了。
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);
}
虽然这依然相当简单,但是它的确多提供了一些有用的信息。图 6 给出了与 图 5 相同的错误,但是这一次错误处理代码向用户或程序员更好地说明了究竟发生了什么。
2.生成请求
实际上生成 HEAD 请求非常简单;您可以使用 "HEAD"(而不是 "GET" 或 "POST")作为第一个参数来调用 open() 方法
使用 Ajax生成一个 HEAD 请求
function getSalesData() { createRequest(); var url = "/boards/servlet/UpdateBoardSales"; request.open("HEAD", url, true); request.onreadystatechange = updatePage; request.send(null); } |
当您这样生成一个 HEAD 请求时,服务器并不会像对 GET 或 POST 请求一样返回一个真正的响应。相反,服务器只会返回资源的 头(header),这包括响应中内容最后修改的时间、请求资源是否存在和很多其他有用信息。您可以在服务器处理并返回资源之前使用这些信息来了解有关资源的信息。
对于这种请求您可以做的最简单的事情就是简单地输出所有的响应头的内容。这可以让您了解通过 HEAD 请求可以使用什么。清单 10 提供了一个简单的回调函数,用来输出从 HEAD 请求中获得的响应头的内容。
输出从 HEAD请求中获得的响应头的内容
function updatePage() { if (request.readyState == 4) { alert(request.getAllResponseHeaders()); } } |
3.检查URL是否存在
function updatePage() { if (request.readyState == 4) { if (request.status == 200) { alert("URL exists"); } else if (request.status == 404) { alert("URL does not exist."); } else { alert("Status is: " + request.status); } } } |
诚实地说,这段代码的价值并不太大。服务器必须对请求进行响应,并构造一个响应来填充内容长度的响应头,因此并不能节省任何处理时间。另外,这花费的时间 与生成请求并使用 HEAD 请求来查看 URL 是否存在所需要的时间一样多,因为它要生成使用 GET 或 POST 的请求,而不仅仅是如 清单 7 所示一样来处理错误代码。不过,有时确切地了解目前什么可用也是非常有用的;您永远不会知道何时创造力就会迸发或者何时需要 HEAD 请求!
有用的HEAD请求
您会发现 HEAD 请求非常有用的一个领域是用来查看内容的长度或内容的类型。这样可以确定是否需要发回大量数据来处理请求,和服务器是否试图返回二进制数据,而不是 HTML、文本或 XML(在 JavaScript 中,这 3 种类型的数据都比二进制数据更容易处理)。
在这些情况中,您只使用了适当的头名,并将其传递给 XMLHttpRequest 对象的 getResponseHeader() 方法。因此要获取响应的长度,只需要调用 request.getResponseHeader("Content-Length");。要获取内容类型,请使用 request.getResponseHeader("Content-Type");。
在 很多应用程序中,生成 HEAD 请求并没有增加任何功能,甚至可能会导致请求速度变慢(通过强制生成一个 HEAD 请求来获取有关响应的数据,然后在使用一个 GET 或 POST 请求来真正获取响应)。然而,在出现您不确定有关脚本或服务器端组件的情况时,使用 HEAD 请求可以获取一些基本的数据,而不需要对响应数据真正进行处理,也不需要大量的带宽来发送响应。
ajax伪代码
// 写一个完整的ajax 请求的代码;
<form action="POST">
<p>Enter your phone number:
<input type="text" size="14" name="phone" id="phone"
onChange="getCustomerInfo();" />
</p>
<p>Your order will be delivered to:</p>
<div id="address"></div>
<p>Type your order in here:</p>
<p><textarea name="order" rows="6" cols="50" id="order"></textarea></p>
</form>
</body>
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!");
}
function getCustomerInfo() {
createRequest();
// Do something with the request variable
var phone = document.getElementById("phone").value;
var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
request.open("GET", url, true);
request.onreadystatechange = updatePage; // 一定要在send之前;
// request.setRequestHeader(‘Content-Type’,’application/x-www-form-urlencoded’); // 默认情况下,服务器对POST请求和提交web表单的请求不会一视同仁。因此,服务器必须有程序来读取发送过来的原始数据,并从中解析出有用的部分。我们可以使用XMLHttpRequest来模仿表单提交;(后台在取数据的时候只要用一个方式即可;)
request.send(null);
}
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 />");
alert("status is " + request.status);
}
}