Drupal专业开发指南(54)-第19章 XML-RPC

3092008 分类: Drupal

Drupal 可与外部系统良好的集成。也就是说,如果存在一个开放的标准,drupal 可以通过内置的或者贡献的模块两种方式支持这一标准。XML-RPC 也不例外。通过本章,你将学习到如何利用 drupal 的发送和接受 XML-RPC 调用的能力。

什么是 XML-RPC

一个远程过程调用(remote procedure call)是指一个程序要求另一个程序执行一个函数。XML-RPC 是调用通过 XML 格式编码并通过 HTTP 发送的远程过程调用的标准。XML-RPC 协议是由 UserLand 软件公司的 Dave Winner 与微软合作创造。它专注用于分布式网络系统之间的相互对话,比如当一个 Drupal 网站需要另一个 Drupal 网站的一些信息时。

注意:远程过程被调用时是作为一个方法被引用的。这就是为什么 XML 编码将远程过程的名字包装在 标签里的原因。

当一个 XML-RPC 发生时,存在着两个参与者。一个是产生请求的网站,成为客户端。接受请求的的作为服务器端。

警告:为了使你的 Drupal 网站作为客户端,它必须具有向外发送 HTTP 请求的能力。一些主机托管公司出于安全原因禁止了这一能力,这使得你的努力不能穿过他们的防火墙。如果你的网站仅作为服务器端使用,这就没有任何担心了, 因为传入的 XML-RPC 请求使用标准的 web 端口(通常为端口80).

XML-RPC 客户端

客户端是将要发送请求的的计算机。它向服务器端发送一个标准的 HTTP POST 请求。这一请求的主体由 XML 组成并且包含一个简单的名为 的标签。在 标签内部,嵌套了两个字标签, 和 。让我们通过一个实例来看一下这是如何工作的。

XML-RPC 客户端例子:获取时间

在网站 http://www.xmlrpc.com 上可以看到 XML-RPC 说明书,它同时也带有了一些用于测试的实现。在我们的第一个例子中,让我们通过 XML-RPC 来向该站点请求当前时间:

$time = xmlrpc(’http://time.xmlrpc.com/RPC2′, ‘currentTime.getCurrentTime’);

在这里你调用了 Drupal 的 xmlrpc() 函数,告诉它链接到服务器 time.xmlrpc.com 的路径 RPC2,并请求服务器端执行一个名为 currentTime.getCurrentTime() 的方法。在这一调用中,你没有使用任何参数。Drupal 将其转化为一个如下所示的 HTTP 请求:

POST /RPC2 HTTP/1.0
Host: time.xmlrpc.com
User-Agent: Drupal (+http://drupal.org/)
Content-Length: 118
Content-Type: text/xml
<?xml version=”1.0″?>

currentTime.getCurrentTime

服务器端 time.xmlrpc.com 非常高兴的执行函数,并返回如下所示的相应给你:

HTTP/1.1 200 OK
Connection: close
Content-Length: 183
Content-Type: text/xml
Date: Fri, 18 April 2007 02:45:36 GMT
Server: UserLand Frontier/9.0.1-WinNT
<?xml version=”1.0″?>

20070418T22:45:36

当响应返回后,Drupal 解析它,将其识别为一个简单的用 ISO8601 国际日期格式表示值,并将此值分配给变量 $time。事实上 Drupal 比这要做的更多,它不仅返回有用的以 ISO8601 格式展示的时间,并且还包括年、月、日、小时、分钟和秒等时间的组成部分。

重要的几点经验如下所示:

你调用一个远方的服务器,然后它回答你。

请求和相应都通过 XML 来描述。

你使用 xmlrpc() 函数,包含一个 URL 和要调用的远程过程的名字。

返回给你的值将通过特定数据类型的标签来标识。

Drupal 自动解析相应。

你仅用一行代码就完成乐者所有的工作。

XML-RPC 客户端例子:获取一个州的州名

让我们尝试一个稍微复杂的例子。它仅仅复杂了一点点,因为你不但发送了你所调用的远程方法的名称,而且还包括了一个参数。UserLand 软件在站点 betty.userland.com 运行了一个 web 服务:它将50个美国的州以字母顺序排列。所以如果你请求第1个州,它返回 Alabama。第50个州为 Wyoming。方法的名称为 examples.getStateName。让我们向它请求列表中的第3个州:

$state_name = xmlrpc(’http://betty.userland.com/RPC2′, ‘examples.getStateName’, 3);

它将 $state_name 设置为 Arizona.下面是 Drupal 发送的 XML(为了简洁,从这里起我们省略了 HTTP 头部)

<?xml version=”1.0″?>

examples.getStateName

3

下面是你从 betty.userland.com 获得的相应:

<?xml version=”1.0″?>

Arizona

注意 Drupal 将自动的发现你发送的参数是一个整数,并在你的请求中以此来对它编码。但是在相应中发生了什么呢?再放回的值的周围没有任何类型标签。应该是这种形式么 Arizona?好的,是的,这个也将正确的工作。但是在 XML-RPC 中,一个没有类型的值将被认为是字符串类型,这样会更加简洁。

在 Drupal 中进行一个 XML-RPC 客户端调用是非常简单的。仅用一行代码:

$result = xmlrpc($url, $method, $param_1, $param_2, $param_3…)

处理 XML-RPC 客户端错误

如果由于一些原因调用失败,xmlrpc() 将返回 DFALSE。为了找出哪里出错了,你可以使用方法 xmlrpc_errorno() 来得到错误码或者使用 xmlrpc_message() 来得到相应的消息。如果你尝试着从 betty.userland.com 得到一个州名但却不给出相应的必须的参数州的代码的话,下面所示就是所要发生的。

$state_name = xmlrpc(’http://betty.userland.com/RPC2′, ‘examples.getStateName’);
if (xmlrpc_error()) {
$error_num = xmlrpc_errno();
$error = xmlrpc_error();
drupal_set_message(t(’Could not get state name because the remote site said: %error’), array(’%error’ => $error->message . ‘(’ . $error_num . ‘)’));
}

这一代码将导致下列的消息出现在用户面前:

Could not get state name because the remote site gave an error: Can’t call “getStateName” because there aren’t enough parameters. (4)

注意当你报告错误的时候,你应该指出3件事情:你想要做什么,为什么你不能完成它,和其他一些你可以获取到的额外信息。通常一个友好的错误将通过 drupal_set_message() 来展现以通知给用户,同时一个更加详细的错误将会写到 watchdog 中去并可通过 http://example.com/?q=admin/logs/watchdog 来查看。

参数类型转换

通常你所调用的远程过程要求参数必须是特定的 XML-RPC 类型,比如整形(integers)或者数组。一种保证这样的方式是使用PHP的类型转换来发送你的参数:

$state_name = xmlrpc(’http://betty.userland.com/RPC2′, ‘examples.getStateName’,(int) $state_num);

一种更好的方式是保证在你代码的其他地方当给变量赋值时,这一变量已经被设置为相应的类型了。

一个简单的 XML-RPC 服务器端

正如在 XML-RPC 客户端例子中所看到的,Drupal 为你做了大部分工作。现在让我们看一个简单的服务器端的例子。你需要做3件事来建立你的服务器端:

1、定义一个当客户请求到来时你想知执行的函数。

2、将函数映射为一个工作的方法名。

3、(可选的)定义一个方法签名。

按照 Drupal 的惯例,你想将你的代码与系统核心分开并将其作为一个模块插入。所以,下面是一个简洁的模块,选择一个随机数字,然后让你通过 XML-RPC 提交一个你猜测的该数字。将其命名为 xmlrpclucky.module 并将其放到你的 Drupal 安装路径下的文件夹 sites/all/modules/custom 下面,放到名为 xmlrpclucky 里面。下面是你的 xmlrpclucky.info 文件:

; $Id$
name = XML-RPC Lucky Number
description = Allows XML-RPC clients to guess a number.
version = $Name$

下面是xmlrpclucky.module:

<?php
// $Id$
/**
* Implementation of hook_xmlrpc().
*
* Maps external names of XML-RPC methods to callback functions.
*/
function xmlrpclucky_xmlrpc() {
return array(’xmlrpclucky.guessLuckyNumber’=>’xmlrpclucky_xmlc_guess_lucky_number’);
}
/**
* Test if given number matches a random lucky number.
*/
function xmlrpclucky_xmlc_guess_lucky_number($guess) {
if ($guess < 1 || $guess > 10) {
return xmlrpc_error(1, t(’Your guess must be between 1 and 10.’));
}
$lucky_number = mt_rand(1, 10);
if ($guess == $lucky_number) {
return t(’Your number matched!’);
}
else {
return t(’Sorry, the number was @num.’, array(’@num’ => $lucky_number));
}
}

xmlrpc 钩子描述了由模块所提供的外部 XML-RPC 方法。在我们的例子中,我们仅提供了一个方法,所以这里仅有一个数组。既然这样方法的名字为: xmlrpclucky.guessLuckyNumber。这是请求者使用的名字,它是完全任意的。一个好的实践是使用“.”分割的字符串,使用你的模 块名作为前半部分,使用一个描述性的动词作为后半部分。

注意:尽管通常在 Drupal 里面避免使用骆驼形式的字符串,但在 XML-RPC 方法名中这是个例外。

数组的第2部分是当对 xmlrpclucky.guessLuckyNumber 的请求到来时所要调用的函数的名称。在我们的例子中,我们将这一函数叫做 xmlrpclucky_xmls_guess_lucky_number()。当你开发模块时,你将写很多的函数。通过在行数名字中包含 “xmls”(XML-RPC Server 的简写),你将在第立即能够告知这个函数与外界交互。类似的,你可以在函数中使用“xmlc”,用以调用其他网站上的方法。当你写一个主要调用自身的模块 时,这是个很好的实践,尽管在另一个网站上。

当你的模块决定一个错误方法时,使用 xmlrpc_error() 来定义一个错误代码和一个用以描述哪里出错的帮助字符串。错误数字代码是任意的并且应用相关的。

假定带有这一个模块的站点位于 example.com 上,现在你可以在一个单独的 Drupal 安装上(比如说,在 example2.com)使用以下代码来测试你的幸运数字。

$url = ‘http://example.com/xmlrpc.php’;
$method_name = ‘xmlrpclucky.guessLuckyNumber’;
$our_guess = 3;
$result = xmlrpc($url, $method_name, $our_guess);

$result 现在是“Sorry, the number was 4”。

在你的 Drupal 安装中,文件 xmlrpc.php 包含了一个 XML-RPC 请求到来时所有运行的代码。它被认作 XML-RPC 的终端。

注意:有些人通过重命名 xmlrpc.php 文件来增加安全性,这样改变了 XML-RPC 终端。这可以阻止恶意的机器人来探测服务器端的 XML-RPC 接口。其他人将完全删除这一文件,如果该站点不接受 XML-RPC 请求的话。

Xmlrpc 钩子有两种形式。在简单的形式中,如例子 xmlrpclucky.module 中所展示的,它简单的将一个外部的方法名映射到一个函数上。在一个更高级的形式中,它描述了方法的方法特征;指的是它返回的是什么 XML-RPC 类型,以及每一个参数的类型(参看 http://www.xmlrpc.com/spec 来查看类型列表)下面是在我们的例子中 xmlrpc 钩子的更复杂的形式。

function xmlrpclucky_xmlrpc() {
return array(
array(
‘xmlrpclucky.guessLuckyNumber’, // External method name.
‘xmlrpclucky_lucky_number’, // Drupal function to run.
array(’string’, ‘int’), // Return value’s type, then any parameter types
t(’Returns a lucky number.’) // Description.
)
);
}

图19-1展示了当一个请求从 XML-RPC 客户端到达我们的模块时 XML-RPC 的请求生命周期。如果你在你的模块中使用更复杂的形式实现 xmlrpc 钩子时,你将得到多个好处。首先,Drupal 将根据方法签名自动验证发送过来的类型并返回 -32603:Server error。如果验证失败将标示出非法的方法参数。同样,Drupal 的内置的方法 system.methodSignature 和 system.methodHelp 将返回关于你的方法的相关信息。

$url = ‘http://example.com/xmlrpc.php’;

//得到服务器端可用的所有的 XML-RPC 的一个数组

$methods = xmlrpc($url, ’system.listMethods’);

//得到我们样例方法的方法签名

$signature = xmlrpc($url, ’system.methodSignature’, ‘xmlrpclucky.guessLuckyNumber’);

//得到我们样例方法的帮助字符串

$help = xmlrpc($url, ’system.methodHelp’, ‘xmlrpclucky.guessLuckyNumber’);

$methods 现在是一个数组: (’system.multicall’, ’system.methodSignature’, ’system.getCapabilities’, ’system.listMethods’, ’system.methodHelp’, ‘xmlrpclucky.guessLuckyNumber’)

$signature现在是一个数组: (’int’)

$help 现在是 “Returns a lucky number.”

图19-1 一个 XML-RPC 请求的处理流程图

其它的值得一提的内置方法为 system.multiCall,它允许在一个 HTTP 请求中调用多个 XML-RPC 方法。关于这一个规定的更多信息(它没有包含在 XML-RPC 说明中),参看:http://web.archive.org/web/20060502175739/ 和 http:// www.xmlrpc.com/discuss/msgReader$1208

小结

当读完这一章后,你应该可以:

  • 从一个 Drupal 站点上发送 XML-RPC 请求到一个不同的服务器上。
  • 实现一个基本的 XML-RPC 服务器。
  • 理解 Drupal 是如何将 XML-RPC 方法映射到 php 函数上的。

相关文章


没有评论

(*)
(不会公布)