📄 掌握 ajax,第 3 部分 ajax 中的高级请求和响应.htm
字号:
的响应文本</B></A><BR><IMG height=334 alt="就绪状态为 3 的响应文本"
src="掌握 Ajax,第 3 部分 Ajax 中的高级请求和响应.files/ready-state_3.jpg"
width=500> <BR>
<P>您会看到就绪状态为 3
的响应在每个脚本、每个服务器甚至每个浏览器上都是不一样的。不过,这在调试应用程序中依然是非常有用的。</P>
<P><A name=N102C5><SPAN class=smalltitle>获取安全数据</SPAN></A></P>
<P>所有的文档和规范都强调,只有在就绪状态为 4 时数据才可以安全使用。相信我,当就绪状态为 3 时,您很少能找到无法从
<CODE>responseText</CODE> 属性获取数据的情况。然而,在应用程序中将自己的逻辑依赖于就绪状态 3
可不是什么好主意 —— 一旦您编写了依赖于就绪状态 3 的完整数据的的代码,几乎就要自己来负责当时的数据不完整问题了。</P>
<P>比较好的做法是向用户提供一些反馈,说明在处于就绪状态 3 时,很快就会有响应了。尽管使用 <CODE>alert()</CODE>
之类的函数显然不是什么好主意 —— 使用 Ajax 然后使用一个警告对话框来阻塞用户显然是错误的 ——
不过您可以在就绪状态发生变化时更新表单或页面中的域。例如,对于就绪状态 1 来说要将进度指示器的宽度设置为 25%,对于就绪状态 2
来说要将进度指示器的宽度设置为 50%,对于就绪状态 3 来说要将进度指示器的宽度设置为 75%,当就绪状态为 4
时将进度指示器的宽度设置为 100%(完成)。</P>
<P>当然,正如您已经看到的一样,这种方法非常聪明,但它是依赖于浏览器的。在 Opera 上,您永远都不会看到前两个就绪状态,而在
Safari 上则没有第一个(1)。由于这个原因,我将这段代码留作练习,而没有在本文中包括进来。</P>
<P>现在应该来看一下状态代码了。</P>
<P><A name=N102DF><SPAN class=atitle>深入了解 HTTP 状态代码</SPAN></A></P>
<P>有了就绪状态和您在 Ajax 编程技术中学习到的服务器的响应,您就可以为 Ajax 应用程序添加另外一级复杂性了 —— 这要使用
HTTP 状态代码。这些代码对于 Ajax 来说并没有什么新鲜。从 Web 出现以来,它们就已经存在了。在 Web
浏览器中您可能已经看到过几个状态代码:</P>
<UL>
<LI><B>401</B>:未经授权
<LI><B>403</B>:禁止
<LI><B>404</B>:没找到 </LI></UL>
<P>您可以找到更多的状态代码(完整清单请参见 <A
href="http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro3/#resources">参考资料</A>)。要为
Ajax 应用程序另外添加一层控制和响应(以及更为健壮的错误处理)机制,您需要适当地查看请求和响应中的状态代码。</P>
<P><A name=N10302><SPAN class=smalltitle>200:一切正常</SPAN></A></P>
<P>在很多 Ajax 应用程序中,您将看到一个回调函数,它负责检查就绪状态,然后继续利用从服务器响应中返回的数据,如 <A
href="http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro3/#listing6">清单
6</A> 所示。</P><BR><BR><A name=listing6><B>清单 6.
忽略状态代码的回调函数</B></A><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode>
function updatePage() {
<SPAN class=boldcode>if (request.readyState == 4) {</SPAN>
var response = request.responseText.split("|");
document.getElementById("order").value = response[0];
document.getElementById("address").innerHTML =
response[1].replace(/\n/g, "<br />");
}
}
</PRE></TD></TR></TBODY></TABLE><BR>
<P>这对于 Ajax 编程来说证明是一种短视而错误的方法。如果脚本需要认证,而请求却没有提供有效的证书,那么服务器就会返回诸如 403
或 401 之类的错误代码。然而,由于服务器对请求进行了应答,因此就绪状态就被设置为
4(即使应答并不是请求所期望的也是如此)。最终,用户没有获得有效数据,当 JavaScript
试图使用不存在的服务器数据时就可能会出现严重的错误。</P>
<P>它花费了最小的努力来确保服务器不但完成了一个请求,而且还返回了一个 “一切良好” 的状态代码。这个代码是 "200",它是通过
<CODE>XMLHttpRequest</CODE> 对象的 <CODE>status</CODE>
属性来报告的。为了确保服务器不但完成了一个请求,而且还报告了一个 OK 状态,请在您的回调函数中添加另外一个检查功能,如 <A
href="http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro3/#listing7">清单
7</A> 所示。</P><BR><BR><A name=listing7><B>清单 7. 检查有效状态代码</B></A><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode>
function updatePage() {
if (request.readyState == 4) {
<SPAN class=boldcode> if (request.status == 200) {</SPAN>
var response = request.responseText.split("|");
document.getElementById("order").value = response[0];
document.getElementById("address").innerHTML =
response[1].replace(/\n/g, "<br />");
<SPAN class=boldcode> } else
alert("status is " + request.status);</SPAN>
}
}
</PRE></TD></TR></TBODY></TABLE><BR>
<P>通过添加这几行代码,您就可以确认是否存在问题,用户会看到一个有用的错误消息,而不仅仅是看到一个由断章取义的数据所构成的页面,而没有任何解释。</P>
<P><A name=N10343><SPAN class=smalltitle>重定向和重新路由</SPAN></A></P>
<P>在深入介绍有关错误的内容之前,我们有必要来讨论一下有关一个在使用 Ajax 时 <I>并不需要</I> 关心的问题 ——
重定向。在 HTTP 状态代码中,这是 300 系列的状态代码,包括:</P>
<UL>
<LI><B>301</B>:永久移动
<LI><B>302</B>:找到(请求被重新定向到另外一个 URL/URI 上)
<LI><B>305</B>:使用代理(请求必须使用一个代理来访问所请求的资源) </LI></UL>
<P>Ajax 程序员可能并不太关心有关重定向的问题,这是由于两方面的原因:</P>
<UL>
<LI>首先,Ajax 应用程序通常都是为一个特定的服务器端脚本、servlet
或应用程序而编写的。对于那些您看不到就消失了的组件来说,Ajax
程序员就不太清楚了。因此有时您会知道资源已经移动了(因为您移动了它,或者通过某种手段移动了它),接下来要修改请求中的
URL,并且不会再碰到这种结果了。
<LI>更为重要的一个原因是:Ajax 应用程序和请求都是封装在沙盒中的。这就意味着提供生成 Ajax 请求的 Web
页面的域必须是对这些请求进行响应的域。因此 ebay.com 所提供的 Web 页面就不能对一个在 amazon.com
上运行的脚本生成一个 Ajax 风格的请求;在 ibm.com 上的 Ajax 应用程序也无法对在 netbeans.org
上运行的 servlets 发出请求。 </LI></UL>
<P>结果是您的请求无法重定向到其他服务器上,而不会产生安全性错误。在这些情况中,您根本就不会得到状态代码。通常在调试控制台中都会产生一个
JavaScript 错误。因此,在对状态代码进行充分的考虑之后,您就可以完全忽略重定向代码的问题了。</P>
<TABLE cellSpacing=0 cellPadding=0 width="50%" align=right
border=0><TBODY>
<TR>
<TD width=10><IMG height=1 alt=""
src="掌握 Ajax,第 3 部分 Ajax 中的高级请求和响应.files/c.gif" width=10></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=1>
<TBODY>
<TR>
<TD bgColor=#eeeeee><A
name=N10374><B>边界情况和困难情况</B></A><BR>
<P>看到现在,一些新手程序员就可能会这究竟是要讨论什么内容。有一点事实大家需要知道:只有不到 5% 的
Ajax 请求需要使用诸如 2、3 之类的就绪状态和诸如 403 之类的状态代码(实际上,这个比率可能更接近于
1% 甚至更少)。这些情况非常重要,称为 <I>边界情况(edge case)</I> ——
它们只会在一些非常特殊的情况下发生,其中遇到的都是最奇特的问题。虽然这些情况并不普遍,但是这些边界情况却占据了大部分用户所碰到的问题的
80%!</P>
<P>对于典型的用户来说,应用程序 100
次都是正常工作的这个事实通常都会被忘记,然而应用程序只要一次出错就会被他们清楚地记住。如果您可以很好地处理边界情况(或困难情况),就可以为再次访问站点的用户提供满意的回报。</P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<P><A name=N10383><SPAN class=smalltitle>错误</SPAN></A></P>
<P>一旦接收到状态代码 200 并且意识到可以很大程度上忽略 300 系列的状态代码之后,所需要担心的唯一一组代码就是 400
系列的代码了,这说明了不同类型的错误。回头再来看一下 <A
href="http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro3/#listing7">清单
7</A>,并注意在对错误进行处理时,只将少数常见的错误消息输出给用户了。尽管这是朝正确方向前进的一步,但是要告诉从事应用程序开发的用户和程序员究竟发生了什么问题,这些消息仍然是没有太大用处的。</P>
<P>首先,我们要添加对找不到的页的支持。实际上这在大部分产品系统中都不应该出现,但是在测试脚本位置发生变化或程序员输入了错误的 URL
时,这种情况并不罕见。如果您可以自然地报告 404
错误,就可以为那些困扰不堪的用户和程序员提供更多帮助。例如,如果服务器上的一个脚本被删除了,我们就可以使用 <A
href="http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro3/#listing7">清单
7</A> 中的代码,这样用户就会看到一个如 <A
href="http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro3/#fig5">图
5</A> 所示的非描述性错误。</P><BR><BR><A name=fig5><B>图 5.
常见错误处理</B></A><BR><IMG height=376 alt=常见错误处理
src="掌握 Ajax,第 3 部分 Ajax 中的高级请求和响应.files/generic_error.jpg"
width=500> <BR>
<P>用户无法判断问题究竟是认证问题、没找到脚本(此处就是这种情况)、用户错误还是代码中有些地方产生了问题。添加一些简单的代码可以让这个错误更加具体。请参照
<A
href="http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro3/#listing8">清单
8</A>,它负责处理没找到的脚本或认证发生错误的情况,在出现这些错误时都会给出具体的消息。</P><BR><BR><A
name=listing8><B>清单 8. 检查有效状态代码</B></A><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode>
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 />");
<SPAN class=boldcode> } else if (request.status == 404) {
alert ("Requested URL is not found.");
} else if (request.status == 403) {
alert("Access denied.");</SPAN>
} else
alert("status is " + request.status);
}
}
</PRE></TD></TR></TBODY></TABLE><BR>
<P>虽然这依然相当简单,但是它的确多提供了一些有用的信息。<A
href="http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro3/#fig6">图
6</A> 给出了与 <A
href="http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro3/#fig5">图
5</A> 相同的错误,但是这一次错误处理代码向用户或程序员更好地说明了究竟发生了什么。</P><BR><BR><A
name=fig6><B>图 6. 特殊错误处理</B></A><BR><IMG height=379 alt=特殊错误处理
src="掌握 Ajax,第 3 部分 Ajax 中的高级请求和响应.files/specific_error.jpg"
width=500> <BR>
<P>在我们自己的应用程序中,可以考虑在发生认证失败的情况时清除用户名和密码,并向屏幕上添加一条错误消息。我们可以使用类似的方法来更好地处理找不到脚本或其他
400 类型的错误(例如 405 表示不允许使用诸如发送 HEAD 请求之类不可接受的请求方法,而 407
则表示需要进行代理认证)。然而不管采用哪种选择,都需要从对服务器上返回的状态代码开始入手进行处理。</P>
<P><A name=N103DC><SPAN class=atitle>其他请求类型</SPAN></A></P>
<P>如果您真希望控制 <CODE>XMLHttpRequest</CODE> 对象,可以考虑最后实现这种功能 —— 将 HEAD
请求添加到指令中。在前两篇文章中,我们已经介绍了如何生成 GET 请求;在马上就要发表的一篇文章中,您会学习有关使用 POST
请求将数据发送到服务器上的知识。不过本着增强错误处理和信息搜集的精神,您应该学习如何生成 HEAD 请求。</P>
<P><A name=N103EA><SPAN class=smalltitle>生成请求</SPAN></A></P>
<P>实际上生成 HEAD 请求非常简单;您可以使用 "HEAD"(而不是 "GET" 或 "POST")作为第一个参数来调用
<CODE>open()</CODE> 方法,如 <A
href="http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro3/#listing9">清单
9</A> 所示。</P><BR><BR><A name=listing9><B>清单 9. 使用 Ajax 生成一个 HEAD
请求</B></A><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -