Ajax 传输方法:准备工作 (1)
准备工作
Ajax 到底是什么,为何这么重要?Ajax 不过是即时更新网页而不需要向服务器请求完整的新页面的一种方法。按照顺序来看,客户机首先从服务器请求页面。服务器返回内嵌 JavaScript 代码的 HTML 页面。然后 JavaScript 代码从服务器请求更多的数据(通常使用 XML)并动态地更新页面。可以用定时器或者根据用户输入触发脚本。脚本如何从服务器获取数据就是本文要讲述的内容。
关于 Ajax 的大部分书籍和文章都提到了一种 Ajax 传输方法:XMLHttp 请求。这可能是因为两种原因:首先这种方法可能是最精巧的,其次它是最新的方法。不过并没有新到要担心浏览器的兼容性。如果浏览器不支持 XMLHttp,也就不大可能很好地支持动态 HTML(DHTML),因而没有必要支持这样的浏览器。
XMLHttp 对象非常灵活,但是由于安全问题必须限制能够检索数据的位置。这些限制看起来似乎无关紧要,但在完成把 Ajax 控件放置到其他服务器的 blog 页面上这类任务时,就会带来实际问题。因此必须考虑替代方法,即速度慢的 <script> 标记。不要忘记还有第三种选择:使用框架或在线框架(iframe)与服务器交换数据。这种方法不算新,但是值得考虑,在需要的时候要想到它。
本教程包含三部分不同的代码。最底层是数据库,包括模式和数据。数据库之上是服务器页面,以不同的形式公开数据库中的数据。再上面是一组 HTML 页面,通过不同的机制访问数据。
这一节首先建立数据库。然后创建访问数据的 PHP 页面。
建立数据库
Web 应用程序怎能没有数据库呢?本教程使用了一个简单的 MySQL 数据库,该数据库只有一个表,即 book。清单 1 显示了该数据库,并定义了记录格式。
清单 1. 数据库
CREATE TABLE books (
id MEDIUMINT NOT NULL AUTO_INCREMENT,
author TEXT,
title TEXT,
PRIMARY KEY ( id ) );
INSERT INTO books VALUES (
null,
"Jack Herrington",
"Code Generation in Action" );
INSERT INTO books VALUES (
null,
"Jack Herrington",
"Podcasting Hacks" );
INSERT INTO books VALUES (
null,
"Jack Herrington",
"PHP Hacks" );
清单 1 没有什么复杂的地方:只是一个包含三个字段的表,这三个字段是惟一标识符、标题和作者。向客户机公开这个表需要几种不同的 Web 页面,以不同形式导出数据。下面几节详细说明每种格式,并给出了用给定格式从数据库中输出数据的 PHP 服务器代码。
构建文本服务
清单 2 中的 PHP 代码连接到数据库,并把数据库中的内容存成文本字符串,记录之间用回车换行分开。
清单 2. 文本格式的服务
<?php
require_once("DB.php");
header( "Content-type: text" );
$dsn = 'mysql://root:password@localhost/ajaxdb';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
$res = $db->query( "SELECT author, title FROM books" );
while( $res->fetchInto( $row ) )
{
echo($row[0].' - '.$row[1]."\n");
}
?>
测试该服务可以使用 php 解释器在命令行中运行,如 清单 3 所示。
清单 3. 运行文本格式的服务
% php text.php
Jack Herrington - Code Generation in Action
Jack Herrington - Podcasting Hacks
Jack Herrington - PHP Hacks
%
这个例子可能看起来无关紧要,但要记住,在 Ajax 世界中,任何非 XML 或 XML 兼容流都被看作是文本。因此传输的 vCard 或者 base64 编码流都是简单的文本。您必须知道如何传递非 XML 数据。
构建 XML 服务
XML 是 Ajax 最常用的信息源。可以使用 清单 4 中所示代码创建该示例所使用的最简单的 XML 版本。
清单 4. Xml.php
<php
header( 'Content-type: text/xml' );
?>
<books>
<?php
require_once("DB.php");
$dsn = 'mysql://root:password@localhost/ajaxdb';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
$res = $db->query( "SELECT author, title FROM books" );
while( $res->fetchInto( $row ) )
{
?>
<book><author><?php echo($row[0]) ?>
</author><title><?php echo($row[1]) ?>
</title></book>
<?php
}
?>
</books>
注意,脚本的开始部分将 Content-type 头字段设置为 text/xml MIME 类型。这种类型对于 Ajax 至关重要。除非正确设置 MIME 类型,否则就不会在 XMLHttp 请求的结尾部分返回 XML 文档。
清单 5 显示了 清单 4 的命令行输出。
清单 5. xml.php 的输出
% php xml.php
<books>
<book><author>Jack Herrington</author><title>Code Generation in Action</title></book>
<book><author>Jack Herrington</author><title>Podcasting Hacks</title></book>
<book><author>Jack Herrington</author><title>PHP Hacks</title></book>
</books>
%
如果使用 iframe 方案就不能直接发送 XML。相反,必须将 XML 编码成 HTML 文本。清单 6 显示了这种编码。
清单 6. Xml_text.php
<?php
header( 'Content-type: text/html' );
ob_start();
?>
<books>
<?php
require_once("DB.php");
$dsn = 'mysql://root:password@localhost/ajaxdb';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
$res = $db->query( "SELECT author, title FROM books" );
while( $res->fetchInto( $row ) )
{
?>
<book><author><?php echo($row[0]) ?>
</author><title><?php echo($row[1]) ?>
</title></book>
<?php
}
?>
</books>
<?php
$xml = ob_get_clean();
$xml = preg_replace( '/\>/', '>', $xml );
$xml = preg_replace( '/\</', '<', $xml );
echo( $xml );
?>
在这种情况下,内容类型被设置为 html,所有的 XML 内容都是使用 ob_start() 和 ob_get_clean() 方法捕获的。然后 XML 通过将 < 和 > 符号改为 < 和 > 来转化成 HTML 字符串。可以在 清单 7 中看到该脚本的输出。
清单 7. xml_text.php 的输出
% php xml_text.php
<books>
<book><author>Jack Herrington</author>
<title>Code Generation in Action</title></book>
<book><author>Jack Herrington</author>
<title>Podcasting Hacks</title></book>
<book><author>Jack Herrington</author>
<title>PHP Hacks</title></book>
</books>
%
处理这种自定义 XML 格式仅仅是了解 XML 过程中的一部分。您的应用程序中也可能需要使用其他的 XML 标准。
构建 RSS 和 RDF 服务
丰富站点摘要(Rich Site Summary,RSS)和资源描述框架(Resource Description Framework,RDF)联合标准有两个突出的优点:它们是基于 XML 的,因此非常适合 Ajax,同时它们很容易创建。清单 8 显示了从数据库表创建 RSS 提要的代码。
清单 8. Rss.php
<?php
header( 'Content-type: text/xml' );
?>
<rss version="0.91">
<channel>
<title>Book List</title>
<description>My book list</description>
<?php
require_once("DB.php");
$dsn = 'mysql://root:password@localhost/ajaxdb';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
$res = $db->query( "SELECT author, title, id FROM books" );
while( $res->fetchInto( $row ) )
{
?>
<item><description>A book by <?php echo($row[0]) ?>
</description><title><?php echo($row[1]) ?>
</title>
<link>http://myhost/book.php?id=<?php echo($row[2]) ?></link>
</item>
<?php
}
?>
</channel>
</rss>
在命令行中运行 rss.php 将看到 清单 9 所示的 RSS 输出。
清单 9. rss.php 的输出
% php rss.php
<rss version="0.91">
<channel>
<title>Book List</title>
<description>My book list</description>
<item><description>A book by Jack Herrington</description>
<title>Code Generation in Action</title>
<link>http://myhost/book.php?id=1</link>
</item>
<item><description>A book by Jack Herrington</description>
<title>Podcasting Hacks</title>
<link>http://myhost/book.php?id=2</link>
</item>
<item><description>A book by Jack Herrington</description>
<title>PHP Hacks</title>
<link>http://myhost/book.php?id=3</link>
</item>
</channel>
</rss>
%
在 Ajax 数据中使用 RSS 最重要的是可以用一个操作完成。JavaScript 代码不仅获得了数据,还创建了联合提要,用户可以使用 RSS 阅读器订阅这些提要。而 RSS 读者越来越多了。目前发布的 Firefox Web 浏览器本身支持 RSS 提要,Internet Explorer 7 也将支持 RSS。
但是如果还有其他数据需要随着每个项一起发送怎么办?只需要添加额外的标记即可,如 清单 10 所示。
清单 10. Rssextra.php
<?php
header( 'Content-type: text/xml' );
?>
<rss version="0.91">
<channel>
<title>Book List</title>
<description>My book list</description>
<?php
require_once("DB.php");
$dsn = 'mysql://root:password@localhost/ajaxdb';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
$res = $db->query( "SELECT author, title, id FROM books" );
while( $res->fetchInto( $row ) )
{
?>
<item><description>A book by <?php echo($row[0]) ?>
</description><title><?php echo($row[1]) ?>
</title>
<link>http://myhost/book.php?id=<?php echo($row[2]) ?></link>
<author><?php echo($row[0]) ?></author>
<id><?php echo($row[2]) ?></id>
</item>
<?php
}
?>
</channel>
</rss>
在命令行中运行 rssextra.php,就会看到包含作者和 ID 数据的新增标记,如 清单 11 所示。
清单 11. rssextra.php 的输出
% php rssextra.php
<rss version="0.91">
<channel>
<title>Book List</title>
<description>My book list</description>
<item><description>A book by Jack Herrington</description>
<title>Code Generation in Action</title>
<link>http://myhost/book.php?id=1</link>
<author>Jack Herrington</author>
<id>1</id>
</item>
<item><description>A book by Jack Herrington</description>
<title>Podcasting Hacks</title>
<link>http://myhost/book.php?id=2</link>
<author>Jack Herrington</author>
<id>2</id>
</item>
<item><description>A book by Jack Herrington</description>
<title>PHP Hacks</title>
<link>http://myhost/book.php?id=3</link>
<author>Jack Herrington</author>
<id>3</id>
</item>
</channel>
</rss>
%
由此可见,可以为 RSS 提要添加额外的标记,多数读者将会忽略这些标记。但是如果对此不满意,还可以使用 RDF,这种相似的联合格式允许您使用其他名称空间添加额外的标记。从而避免违反规范。清单 12 显示了生成 RDF 提要的代码。
清单 12. Rdf.php
<?php
header( 'Content-type: text/xml' );
echo( '<?xml version="1.0" ?>'."\n" );
?>
<rdf xmlns:mc="http://mysite.com">
<channel>
<title>Book List</title>
<description>My book list</description>
<link>http://mysite.com/</list>
</channel>
<?php
require_once("DB.php");
$dsn = 'mysql://root:password@localhost/ajaxdb';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
$res = $db->query( "SELECT author, title, id FROM books" );
while( $res->fetchInto( $row ) )
{
?>
<item><description>A book by <?php echo($row[0]) ?>
</description><title><?php echo($row[1]) ?>
</title>
<link>http://mysite.com/book.php?id=<?php echo($row[2]) ?></link>
<mc:author><?php echo($row[0]) ?></mc:author>
<mc:id><?php echo($row[2]) ?></mc:id>
</item>
<?php
}
?>
</rdf>
运行 rdf.php 将看到 清单 13 所示的 RDF 输出。
清单 13. rdf.php 的输出
% php rdf.php
<?xml version="1.0" ?>
<rdf xmlns:mc="http://mysite.com">
<channel>
<title>Book List</title>
<description>My book list</description>
<link>http://mysite.com/</list>
</channel>
<item><description>A book by Jack Herrington</description>
<title>Code Generation in Action</title>
<link>http://mysite.com/book.php?id=1</link>
<mc:author>Jack Herrington</mc:author>
<mc:id>1</mc:id>
</item>
<item><description>A book by Jack Herrington</description>
<title>Podcasting Hacks</title>
<link>http://mysite.com/book.php?id=2</link>
<mc:author>Jack Herrington</mc:author>
<mc:id>2</mc:id>
</item>
<item><description>A book by Jack Herrington</description>
<title>PHP Hacks</title>
<link>http://mysite.com/book.php?id=3</link>
<mc:author>Jack Herrington</mc:author>
<mc:id>3</mc:id>
</item>
</rdf>
%
该脚本在 <rdf> 标记中定义了单独的名称空间 mc。名称空间是否连接到真正的站点实际上并不重要。重要的是利用该名称空间定义了两个额外的标记:<mc:author> 和 <mc:id>,在每个项中添加了作者和 ID 数据。
构建 JavaScript (JSON) 服务
JavaScript 编码格式在 Ajax 开发中大受欢迎。经常被称作 JavaScript Object Notation 格式 或 JSON,但实际上那就是 JavaScript。清单 14 显示了这种数据服务脚本的第一个版本。
清单 14. Js.php
<?php
require_once("DB.php");
header( "Content-type: text/javascript" );
$dsn = 'mysql://root:password@localhost/ajaxdb';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
$res = $db->query( "SELECT author, title FROM books" );
$items = array();
while( $res->fetchInto( $row ) )
{
$items []= "{ author: '".$row[0]."', title: '".$row[1]."' }";
}
?>
[ <?php echo( join( ",\n", $items ) ); ?> ];
注意,这里 MIME 类型被设置为 text/javascript,以便浏览器能够识别。运行该脚本将得到 清单 15 所示的输出。
清单 15. js.php 的输出
% php js.php
[ { author: 'Jack Herrington', title: 'Code Generation in Action' },
{ author: 'Jack Herrington', title: 'Podcasting Hacks' },
{ author: 'Jack Herrington', title: 'PHP Hacks' } ];
%
这种输出不过是一个数组,每个记录都有对应的一个数组(或散列表)。这种输出形式非常适合 JavaScript eval 函数求值。
不幸的是,这个例子不能使用 <script> 标记,因为这段 JavaScript 代码除了创建一个数组外什么也没做。为了能在 <script> 标记中使用,JavaScript 代码必须对数据调用某个方法或函数,如 清单 16 所示。
清单 16. Jsadd.php
<?php
require_once("DB.php");
header( "Content-type: text/javascript" );
$dsn = 'mysql://root:password@localhost/ajaxdb';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
$res = $db->query( "SELECT author, title FROM books" );
$items = array();
while( $res->fetchInto( $row ) )
{
$items []= "{ author: '".$row[0]."', title: '".$row[1]."' }";
}
?>
addData( 'jsadd.php', [ <?php echo( join( ",\n", $items ) ); ?> ] );
这段代码和 清单 14 基本相同,只不过它对数据调用了 addData 函数。在命令行中运行 jsadd.php 将得到 清单 17 所示的输出。
清单 17. jsadd.php 的输出
% php jsadd.php
addData( 'jsadd.php', [ { author: 'Jack Herrington',
title: 'Code Generation in Action' },
{ author: 'Jack Herrington', title: 'Podcasting Hacks' },
{ author: 'Jack Herrington', title: 'PHP Hacks' } ] );
%
这段代码非常适合 <script> 标记,但是 iframes 又如何呢?要将 JavaScript 数据传递给 iframe,JavaScript 数据必须用 HTML 编码,如 清单 18 所示。
清单 18. Js_text.php
<?php
require_once("DB.php");
header( "Content-type: text/html" );
$dsn = 'mysql://root:password@localhost/ajaxdb';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
$res = $db->query( "SELECT author, title FROM books" );
$items = array();
while( $res->fetchInto( $row ) )
{
$items []= "{ author: '".$row[0]."', title: '".$row[1]."' }";
}
?>
[ <?php echo( join( ",\n", $items ) ); ?> ];
与 清单 14 相比,这里惟一的变化是 MIME 类型变成了 html。清单 19 显示了 js_text.php 的输出。
清单 19. js_text.php 的输出
% php js_text.php
[ { author: 'Jack Herrington', title: 'Code Generation in Action' },
{ author: 'Jack Herrington', title: 'Podcasting Hacks' },
{ author: 'Jack Herrington', title: 'PHP Hacks' } ];
%
JavaScript 数据在 Ajax 世界中的真正优势是速度。在一般快的机器上解析 XML 可能需要半分钟,而用编码为 JavaScript 的同样数量的数据生成数组和关联数组只需要几毫秒的时间。
处理的下一步是为这些数据形式构建客户机。
搜索更多相关主题的帖子:
Ajax JavaScript XMLHttp