现在的位置: 首页 > 综合 > 正文

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

2013年03月02日 ⁄ 综合 ⁄ 共 7078字 ⁄ 字号 评论关闭

                              
19章

                              
XML-RPC

译者:老葛

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

 

什么是XML-RPC

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

 

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

 

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

 

警告: 为了使你的Drupal网站作为客户端,它必须具有向外发送HTTP请求的能力。一些主机托管公司出于安全原因禁止了这一能力,这使得你的努力不能穿过他们的防火墙。如果你的网站仅作为服务器端使用,

这就没有任何担心了,因为传入的XML-RPC请求使用标准的web端口(通常为端口80).

 

XML-RPC 客户端

 

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

 

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"?>

<methodCall>

<methodName>currentTime.getCurrentTime</methodName>

<params></params>

</methodCall>

 

服务器端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"?>

<methodResponse>

<params>

<param>

<value>

<dateTime.iso8601>20070418T22:45:36</dateTime.iso8601>

</value>

</param>

</params>

</methodResponse>

 

当响应返回后,Drupal解析它,将其识别为一个简单的用ISO8601国际日期格式表示值,并将此值分配给变量$time.事实上Drupa比这要做的更多,它不仅返回有用的以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"?>

<methodCall>

<methodName>examples.getStateName</methodName>

<params>

<param>

<value>

<int>3</int>

</value>

</param>

</params>

</methodCall>

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

<?xml version="1.0"?>

<methodResponse>

<params>

<param>

<value>Arizona</value>

</param>

</params>

</methodResponse>

注意Drupal将自动的发现你发送的参数是一个整数,并在你的请求中以此来对它编码。但是在相应中发生了什么呢?再放回的值的周围没有任何类型标签。应该是这种形式么<value><string>Arizona</string></value>?好的,是的,这个也将正确的工作。但是在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!');

}

296
CHAPTER 19
XML-RPC

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.methodSignaturesystem.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请求的处理流程图

抱歉!评论已关闭.