掌握 Ajax,第 3 部分:Ajax 中的高级请求和响应全面理解 HTTP 的状态代码、就绪状态和 XMLHttpRequest 对象 |
级别: 初级 Brett McLaughlin(brett@newInstance.com),作家,编辑,O'Reilly Media Inc. 2006 年 3 月 23 日 对 于很多 Web 开发人员来说,只需要生成简单的请求并接收简单的响应即可;但是对于希望掌握 Ajax 的开发人员来说,必须要全面理解 HTTP 状态代码、就绪状态和 XMLHttpRequest 对象。在本文中,Brett McLaughlin 将向您介绍各种状态代码,并展示浏览器如何对其进行处理,本文还给出了在 Ajax 中使用的比较少见的 HTTP 请求。 在本系列的上篇文章中,我们将详细介绍 在本文中,我将在上一篇文章的基础上重点介绍这个请求对象的 3 个关键部分的内容: 这三部分内容都是在构造一个请求时所要考虑的因素;但是介绍这些主题的内容太少了。然而,如果您不仅仅是想了解 Ajax 编程的常识,而是希望了解更多内容,就需要熟悉就绪状态、状态代码和请求本身的内容。当应用程序出现问题时 —— 这种问题总是存在 —— 那么如果能够正确理解就绪状态、如何生成一个 HEAD 请求或者 400 的状态代码的确切含义,就可以在 5 分钟内调试出问题,而不是在各种挫折和困惑中度过 5 个小时。
下面让我们首先来看一下 HTTP 就绪状态。 您应该还记得在上一篇文章中 清单 1. 在回调函数中处理服务器的响应
这显然是就绪状态最常见(也是最简单)的用法。正如您从数字 "4" 中可以看出的一样,还有其他几个就绪状态(您在上一篇文章中也看到过这个清单 —— 请参见参考资料):
open() )。
@H_301_116@1:请求已经建立,但是还没有发送(还没有调用send() )。
@H_301_116@2:请求已发送,正在处理中(通常现在可以从响应中获取内容头)。
@H_301_116@3:请求在处理中;通常响应中已有部分数据可用了,但是服务器还没有完成响应的生成。
@H_301_116@4:响应已完成;您可以获取并使用服务器的响应了。
如果您希望不仅仅是了解 Ajax 编程的基本知识,那么就不但需要知道这些状态,了解这些状态是何时出现的,以及如何来使用这些状态。首先,您需要学习在每种就绪状态下可能碰到的是哪种请求状态。不幸的是,这一点并不直观,而且会涉及几种特殊的情况。 第一种就绪状态的特点是 不过为了满足我们的兴趣,请参见清单 2的内容,其中显示了如何在 readyState 被设置为 0 时来获取这种就绪状态。 清单 2. 获取 0 就绪状态
在这个简单的例子中, 图 1. 就绪状态 0
显然,这并不能为您带来多少好处;需要确保尚未调用 除了 0 就绪状态之外,请求对象还需要依次经历典型的请求和响应的其他几种就绪状态,最后才以就绪状态 4 的形式结束。这就是为什么您在大部分回调函数中都可以看到 要查看这种状态发生的过程非常简单。如果就绪状态为 4,我们不仅要运行回调函数中的代码,而且还要在每次调用回调函数时都输出就绪状态。清单 3给出了一个实现这种功能的例子。 清单 3. 查看就绪状态
如果您不确定如何运行这个函数,就需要创建一个函数,然后在 Web 页面中调用这个函数,并让它向服务器端的组件发送一个请求(例如清单 2给出的函数,或本系列文章的第 1 部分和第 2 部分中给出的例子)。确保在建立请求时,将回调函数设置为 这段代码就是 图 2. 就绪状态 1 您可以自己尝试运行这段代码。将其放入 Web 页面中,然后激活事件处理程序(单击按钮,在域之间按 tab 键切换焦点,或者使用设置的任何方法来触发请求)。这个回调函数会运行多次 —— 每次就绪状态都会改变 —— 您可以看到每个就绪状态的警告。这是跟踪请求所经历的各个阶段的最好方法。 在对这个过程有一个基本的了解之后,请试着从几个不同的浏览器中访问您的页面。您应该会注意到各个浏览器如何处理这些就绪状态并不一致。例如,在 Firefox 1.5 中,您会看到以下就绪状态: 这并不奇怪,因为每个请求状态都在这里表示出来了。然而,如果您使用 Safari 来访问相同的应用程序,就应该看到 —— 或者看不到 —— 一些有趣的事情。下面是在 Safari 2.0.1 中看到的状态: Safari 实际上把第一个就绪状态给丢弃了,也并没有什么明显的原因说明为什么要这样做;不过这就是 Safari 的工作方式。这还说明了一个重要的问题:尽管在使用服务器上的数据之前确保请求的状态为 4 是一个好主意,但是依赖于每个过渡期就绪状态编写的代码的确会在不同的浏览器上得到不同的结果。 例如,在使用 Opera 8.5 时,所显示的就绪状态情况就更加糟糕了: 最后,Internet Explorer 会显示如下状态: 如果您碰到请求方面的问题,这就是用来发现问题的首要之处。最好的方式是在 Internet Explorer 和 Firefox 都进行一下测试 —— 您会看到所有这 4 种状态,并可以检查请求的每个状态所处的情况。 接下来我们再来看一下响应端的情况。 一旦我们理解在请求过程中发生的各个就绪状态之后,接下来就可以来看一下 清单 4. 使用服务器上返回的响应
清单 1相当简单;清单 4稍微有点复杂,但是它们在开始时都要检查就绪状态,并获取 与就绪状态类似, 清单 5. 测试 responseText 属性
现在在浏览器中打开 Web 应用程序,并激活您的请求。要更好地看到这段代码的效果,请使用 Firefox 或 Internet Explorer,因为这两个浏览器都可以报告出请求过程中所有可能的就绪状态。例如在就绪状态 2 中,就没有定义 图 3. 就绪状态为 2 的响应文本 不过在就绪状态 3 中,服务器已经在 图 4. 就绪状态为 3 的响应文本 您会看到就绪状态为 3 的响应在每个脚本、每个服务器甚至每个浏览器上都是不一样的。不过,这在调试应用程序中依然是非常有用的。 所有的文档和规范都强调,只有在就绪状态为 4 时数据才可以安全使用。相信我,当就绪状态为 3 时,您很少能找到无法从 比较好的做法是向用户提供一些反馈,说明在处于就绪状态 3 时,很快就会有响应了。尽管使用 当然,正如您已经看到的一样,这种方法非常聪明,但它是依赖于浏览器的。在 Opera 上,您永远都不会看到前两个就绪状态,而在 Safari 上则没有第一个(1)。由于这个原因,我将这段代码留作练习,而没有在本文中包括进来。 现在应该来看一下状态代码了。 有了就绪状态和您在 Ajax 编程技术中学习到的服务器的响应,您就可以为 Ajax 应用程序添加另外一级复杂性了 —— 这要使用 HTTP 状态代码。这些代码对于 Ajax 来说并没有什么新鲜。从 Web 出现以来,它们就已经存在了。在 Web 浏览器中您可能已经看到过几个状态代码: 您可以找到更多的状态代码(完整清单请参见参考资料)。要为 Ajax 应用程序另外添加一层控制和响应(以及更为健壮的错误处理)机制,您需要适当地查看请求和响应中的状态代码。 在很多 Ajax 应用程序中,您将看到一个回调函数,它负责检查就绪状态,然后继续利用从服务器响应中返回的数据,如清单 6所示。 清单 6. 忽略状态代码的回调函数
这对于 Ajax 编程来说证明是一种短视而错误的方法。如果脚本需要认证,而请求却没有提供有效的证书,那么服务器就会返回诸如 403 或 401 之类的错误代码。然而,由于服务器对请求进行了应答,因此就绪状态就被设置为 4(即使应答并不是请求所期望的也是如此)。最终,用户没有获得有效数据,当 JavaScript 试图使用不存在的服务器数据时就可能会出现严重的错误。 它花费了最小的努力来确保服务器不但完成了一个请求,而且还返回了一个 “一切良好” 的状态代码。这个代码是 "200",它是通过 清单 7. 检查有效状态代码
通过添加这几行代码,您就可以确认是否存在问题,用户会看到一个有用的错误消息,而不仅仅是看到一个由断章取义的数据所构成的页面,而没有任何解释。 在深入介绍有关错误的内容之前,我们有必要来讨论一下有关一个在使用 Ajax 时并不需要关心的问题 —— 重定向。在 HTTP 状态代码中,这是 300 系列的状态代码,包括:
Ajax 程序员可能并不太关心有关重定向的问题,这是由于两方面的原因:
结果是您的请求无法重定向到其他服务器上,而不会产生安全性错误。在这些情况中,您根本就不会得到状态代码。通常在调试控制台中都会产生一个 JavaScript 错误。因此,在对状态代码进行充分的考虑之后,您就可以完全忽略重定向代码的问题了。
一旦接收到状态代码 200 并且意识到可以很大程度上忽略 300 系列的状态代码之后,所需要担心的唯一一组代码就是 400 系列的代码了,这说明了不同类型的错误。回头再来看一下清单 7,并注意在对错误进行处理时,只将少数常见的错误消息输出给用户了。尽管这是朝正确方向前进的一步,但是要告诉从事应用程序开发的用户和程序员究竟发生了什么问题,这些消息仍然是没有太大用处的。 首先,我们要添加对找不到的页的支持。实际上这在大部分产品系统中都不应该出现,但是在测试脚本位置发生变化或程序员输入了错误的 URL 时,这种情况并不罕见。如果您可以自然地报告 404 错误,就可以为那些困扰不堪的用户和程序员提供更多帮助。例如,如果服务器上的一个脚本被删除了,我们就可以使用清单 7中的代码,这样用户就会看到一个如图 5所示的非描述性错误。 图 5. 常见错误处理 用户无法判断问题究竟是认证问题、没找到脚本(此处就是这种情况)、用户错误还是代码中有些地方产生了问题。添加一些简单的代码可以让这个错误更加具体。请参照清单 8,它负责处理没找到的脚本或认证发生错误的情况,在出现这些错误时都会给出具体的消息。 清单 8. 检查有效状态代码
虽然这依然相当简单,但是它的确多提供了一些有用的信息。图 6给出了与图 5相同的错误,但是这一次错误处理代码向用户或程序员更好地说明了究竟发生了什么。 图 6. 特殊错误处理 在我们自己的应用程序中,可以考虑在发生认证失败的情况时清除用户名和密码,并向屏幕上添加一条错误消息。我们可以使用类似的方法来更好地处理找不 到脚本或其他 400 类型的错误(例如 405 表示不允许使用诸如发送 HEAD 请求之类不可接受的请求方法,而 407 则表示需要进行代理认证)。然而不管采用哪种选择,都需要从对服务器上返回的状态代码开始入手进行处理。 如果您真希望控制 实际上生成 HEAD 请求非常简单;您可以使用 "HEAD"(而不是 "GET" 或 "POST")作为第一个参数来调用 清单 9. 使用 Ajax 生成一个 HEAD 请求
当您这样生成一个 HEAD 请求时,服务器并不会像对 GET 或 POST 请求一样返回一个真正的响应。相反,服务器只会返回资源的头(header),这包括响应中内容最后修改的时间、请求资源是否存在和很多其他有用信息。您可以在服务器处理并返回资源之前使用这些信息来了解有关资源的信息。 对于这种请求您可以做的最简单的事情就是简单地输出所有的响应头的内容。这可以让您了解通过 HEAD 请求可以使用什么。清单 10提供了一个简单的回调函数,用来输出从 HEAD 请求中获得的响应头的内容。 清单 10. 输出从 HEAD 请求中获得的响应头的内容
请参见图 7,其中显示了从一个向服务器发出的 HEAD 请求的简单 Ajax 应用程序返回的响应头。 图 7. HEAD 请求的响应头 您可以单独使用这些头(从服务器类型到内容类型)在 Ajax 应用程序中提供其他信息或功能。 您已经看到了当 URL 不存在时应该如何检查 404 错误。如果这变成一个常见的问题 —— 可能是缺少了一个特定的脚本或 servlet —— 那么您就可能会希望在生成完整的 GET 或 POST 请求之前来检查这个 URL。要实现这种功能,生成一个 HEAD 请求,然后在回调函数中检查 404 错误;清单 11给出了一个简单的回调函数。 清单 11. 检查某个 URL 是否存在
诚实地说,这段代码的价值并不太大。服务器必须对请求进行响应,并构造一个响应来填充内容长度的响应头,因此并不能节省任何处理时间。另外,这花费 的时间与生成请求并使用 HEAD 请求来查看 URL 是否存在所需要的时间一样多,因为它要生成使用 GET 或 POST 的请求,而不仅仅是如清单 7所示一样来处理错误代码。不过,有时确切地了解目前什么可用也是非常有用的;您永远不会知道何时创造力就会迸发或者何时需要 HEAD 请求! 您会发现 HEAD 请求非常有用的一个领域是用来查看内容的长度或内容的类型。这样可以确定是否需要发回大量数据来处理请求,和服务器是否试图返回二进制数据,而不是 HTML、文本或 XML(在 JavaScript 中,这 3 种类型的数据都比二进制数据更容易处理)。 在这些情况中,您只使用了适当的头名,并将其传递给 在很多应用程序中,生成 HEAD 请求并没有增加任何功能,甚至可能会导致请求速度变慢(通过强制生成一个 HEAD 请求来获取有关响应的数据,然后在使用一个 GET 或 POST 请求来真正获取响应)。然而,在出现您不确定有关脚本或服务器端组件的情况时,使用 HEAD 请求可以获取一些基本的数据,而不需要对响应数据真正进行处理,也不需要大量的带宽来发送响应。 对于很多 Ajax 和 Web 程序员来说,本文中介绍的内容似乎是太高级了。生成 HEAD 请求的价值是什么呢?到底在什么情况下需要在 JavaScript 中显式地处理重定向状态代码呢?这些都是很好的问题;对于简单的应用程序来说,答案是这些高级技术的价值并不是非常大。 然而,Web 已经不再是只需实现简单应用程序的地方了;用户已经变得更加高级,客户期望能够获得更好的稳定性、更高级的错误报告,如果应用程序有 1% 的时间停机,那么经理就可能会因此而被解雇。 因此您的工作就不能仅仅局限于简单的应用程序了,而是需要更深入理解
本文的目的并非是要让您的应用程序显得十分华丽,而是帮助您去掉黄色聚光灯后重点昭显文字的美丽,或者外观更像桌面一样。尽管这些都是 Ajax 的功能(在后续几篇文章中就会介绍),不过它们却像是蛋糕表面的一层奶油。如果您可以使用 Ajax 来构建一个坚实的基础,让应用程序可以很好地处理错误和问题,用户就会返回您的站点和应用程序。在接下来的文章中,我们将添加这种直观的技巧,这会让客户 兴奋得发抖。(认真地说,您一定不希望错过下一篇文章!)
学习
@H_301_116@“Ajax 简介:理解 Ajax 及其工作原理,构建网站的一种有效方法”(developerWorks,2005 年 12 月):在本系列的第 1 部分中,介绍了 Ajax 组件技术如何一起工作,并展示了 Ajax 的重要概念,包括 XMLHttpRequest 对象。@H_301_116@“使用 JavaScript 和 Ajax 发出异步请求:在 Web 请求中使用 XMLHttpRequest”(developerWorks,2006 年 1 月):在本系列的第 2 部分中,学习如何以一种跨浏览器的方法来创建 XMLHttpRequest 实例,构建并发送请求,以及对服务器进行响应。@H_301_116@“面向 Java 开发人员的 Ajax: 构建动态的 Java 应用程序 Ajax 为更好的 Web 应用程序铺平了道路”(developerWorks,2005 年 9 月):以 Java 的视角从服务器端来看 Ajax,并寻找一种创新的方法来建立动态 Web 应用程序体验。 @H_301_116@“面向 Java 开发人员的 Ajax: Ajax 的 Java 对象序列化”(developerWorks,2005 年 10 月):依次介绍了 5 种进行 Java 对象序列化的方法,并介绍了如何通过网络来发送对象并与 Ajax 进行交互。 @H_301_116@“使用 AJAX 调用 SOAP Web 服务,第 1 部分: 构建 Web 服务客户机”(developerWorks,2005 年 10 月):在这篇有关使用现有的基于 SOAP 的 Web 服务与 Ajax 进行介绍的高级文章中,我们可以看到如何使用 Ajax 设计模式来实现一个基于 Web 浏览器的 SOAP Web 服务客户机。 @H_301_116@Google GMail:查看这个基于 Ajax 的应用程序修改 Web 工作方式的例子。Google Maps是另外一个基于 Google 的 Web 2.0 应用程序。 @H_301_116@Flickr:请参见这个使用 Ajax 来为基于 Web 的应用程序创建桌面外观的例子。 @H_301_116@“Ajax: A New Approach to Web Applications”:请阅读这篇有关 Ajax moniker 的文章 —— 这是所有 Ajax 开发人员的必读物。 @H_301_116@HTTP status codes:从 W3C 上获取一个完整的清单。 @H_301_116@Head Rush Ajax,Elisabeth Freeman、Eric Freeman 和 Brett McLaughlin 著(2006 年 2 月,O'Reilly Media,Inc.):将本文中的思想加入您的大脑中,Head First 风格。 @H_301_116@Java and XML,Second Edition by Brett McLaughlin(2001 年 8 月,O'Reilly Media,Inc.):查看作者对 XHTML 和 XML 转换的讨论。 @H_301_116@JavaScript: The Definitive Guide,David Flanagan(2001 年 11 月,O'Reilly Media,Inc.):深入介绍有关使用 JavaScript 和动态 Web 页面的知识。将要出版的版本增加了两章有关 Ajax 的内容。 @H_301_116@Head First HTML with CSS & XHTML,Elizabeth and Eric Freeman(2005 年 12 月,O'Reilly Media,Inc.):要学习 XHTML、CSS 以及如何配对使用这两种技术的知识,就请仔细阅读这个完整的资源。 @H_301_116@developerWorks Web architecture zone:扩展您构建 Web 的技能。 @H_301_116@developerWorks 技术事件和 Webcasts:随时关注这些为技术开发人员准备的软件情况介绍。 |