XML-RPC 协议是现在大多数 blog 站点支持的标准协议。通过使用该协议,我们可以很方便地使用离线编辑客户端编辑好文章,然后直接上传到 blog 系统进行发布。豆子目前正在进行的一个项目,OrbitsWriter 就是做得类似的工作。尽管完成度不高,但是 XML-RPC 一节已经调通。如果对该项目有任何建议或意见,可以在 GitHub 上找到项目地址。
本文所叙述的实现,即是豆子用在 OrbitsWriter 中的代码。如果需要,完全可以很方便直接从中分离开来。希望这里描述的一种实现,能够对更多的项目有所帮助。
首先来介绍一下 XML-RPC 协议。XML-RPC 是一个通过 Internet 进行的远程过程调用协议。每一个 XML-RPC 消息都是一个 HTTP-POST 请求;请求体则是 XML 格式的。过程在服务器上执行,其返回值同样是 XML 格式。这意味着,我们需要使用 QtNetwork 和 QtXml 两个模块来实现这个客户端。
XML-RPC 协议的过程参数应该是标量,例如数字、字符串、日期等,也可以是复杂的结构体或者数组等。
XML-RPC 请求
下面是一个合法的 XML-RPC 请求:
POST /RPC2 HTTP/1.0
User-Agent: Frontier/5.1.2 (WinNT)
Host: betty.userland.com
Content-Type: text/xml
Content-length: 181
<?xml version="1.0"?>
<methodCall>
<methodName>examples.getStateName</methodName>
<params>
<param>
<value><i4>41</i4></value>
</param>
</params>
</methodCall> 请求头
请求头的第一行 URI 的格式并没有指定:可以是空的,可以只有一个斜线(当服务器仅处理 XML-RPC 调用时)。不过,普遍情况应该是服务器处理很多类型的 HTTP 请求。在这种情况下,我们就得利用这个 URI 识别出,这个请求应该由 XML-RPC 处理代码进行处理。在这个例子中,URI 是 /RPC2,也就是说,需要将这个请求分发给 RPC2 这个响应函数。
User-Agent 和 Host 必须指定。
Content-Type 必须是 text/xml。
Content-Length 必须指定,并且必须是正确的。
有效信息格式
有效信息以 XML 的格式发送,其根标签是<methodCall>。
<methodCall>必须包含一个<methodName>子项,类型是一个字符串,包含需要调用的函数名称。这个字符串只能包含可识别字符:大写和小写的 A-Z,数字 0-9,下划线,点号,冒号和斜线。这个名字由服务器进行解释。例如,methodName 可以是包含可执行代码的脚本文件的文件名,可以是数据库表的单元格名,也可以是包含了子目录和子文件的文件夹名。
如果过程调用需要参数,<methodCall>必须包含一个<params>子项。<params>则包含若干<param>子项,每一个<param>都要有一个<value>标签。
<value>
<value>可以是任何标量,类型允许嵌套。合法的子标签如下表所示:
| 标签 | 类型 | 示例 |
<i4>或者<int> | 四字节有符号整数 | -12 |
<boolean> | 0 (false) 或者 1 (true) | 1 |
<string> | 字符串 | hello world |
<double> | 双精度有符号浮点数 | -12.214 |
<dateTime.iso8601> | 日期/时间 | 19980717T14:08:55 |
<base64> | base64 编码的二进制数据 | eW91IGNhbid0IHJlYWQgdGhpcyE= |
如果不能识别该类型,则当做 string 处理。
<struct>
值类型也可以是<struct>。
<struct>标签包含若干<member>,每一个<member>都要有一个<name>和一个<value>。
下面是拥有两个元素的<struct>:
<struct>
<member>
<name>lowerBound</name>
<value><i4>18</i4></value>
</member>
<member>
<name>upperBound</name>
<value><i4>139</i4></value>
</member>
</struct> <struct>可以嵌套。任何<value>都可以包含另外的<struct>或者其它类型,包括下面介绍的<array>。
<array>
值类型也可以是<array>。<array>包含一个<data>子标签,<data>则包含若干个<value>。
例如,下面的示例是一个包含了四个元素的数组:
<array>
<data>
<value><i4>12</i4></value>
<value><string>Egypt</string></value>
<value><boolean>0</boolean></value>
<value><i4>-31</i4></value>
</data>
</array> <array> 标签没有名字。
正如上面的代码所示,我们可以将不同类型的数据组装成一个数组。
<arrays>也可以嵌套,任何值都可以包含<array>或其它类型,包括<struct>。
XML-RPC 响应
下面是一个合法的 XML-RPC 响应:
HTTP/1.1 200 OK
Connection: close
Content-Length: 158
Content-Type: text/xml
Date: Fri, 17 Jul 1998 19:55:08 GMT
Server: UserLand Frontier/5.1.2-WinNT
<?xml version="1.0"?>
<methodResponse>
<params>
<param>
<value><string>South Dakota</string></value>
</param>
</params>
</methodResponse> 响应格式
第一行,除非有其它服务器错误,返回值总是 200 OK。
Content-Type 是 text/xml。Content-Length 必须指定,并且是正确的。
响应体是一个 XML 结构,根标签是<methodResponse>,包含一个<params>标签,该标签又包含一个<value>。
<methodResponse>也可能包含一个<fault>标签,这个<fault>包含一个<value>,该标签是一个<struct>,包含两个元素:<faultCode>,<int>类型以及<faultString>,<string>类型。
<methodResponse>不允许同时包含<fault>和<params>。
<fault> 示例
HTTP/1.1 200 OK
Connection: close
Content-Length: 426
Content-Type: text/xml
Date: Fri, 17 Jul 1998 19:55:02 GMT
Server: UserLand Frontier/5.1.2-WinNT
<?xml version="1.0"?>
<methodResponse>
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>4</int></value>
</member>
<member>
<name>faultString</name>
<value><string>Too many parameters.</string></value>
</member>
</struct>
</value>
</fault>
</methodResponse>