基于 Qt 的 XML-RPC 客户端:XML-RPC 协议

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>

Leave a Reply