21.26. xmlrpc.client — XML-RPC 客户端访问

源代码: Lib/xmlrpc/client.py


XML-RPC 是一种远程过程调用方法,它以使用 HTTP(S) 传递的 XML 作为载体。 通过它,客户端可以在远程服务器(服务器以 URI 指明)上调用带参数的方法并获取结构化的数据。 本模块支持编写 XML-RPC 客户端代码;它会处理在通用 Python 对象和 XML 之间进行在线翻译的所有细节。

警告

xmlrpc.client 模块对于恶意构建的数据是不安全的。 如果你需要解析不受信任或未经身份验证的数据,请参阅 XML 漏洞

在 3.5 版更改: 对于 HTTPS URI,现在 xmlrpc.client 默认会执行所有必要的证书和主机名检查。

class xmlrpc.client.ServerProxy(uri, transport=None, encoding=None, verbose=False, allow_none=False, use_datetime=False, use_builtin_types=False, *, context=None)

在 3.3 版更改: 增加了 use_builtin_types 旗标。

ServerProxy 实例是管理与远程 XML-RPC 服务器通信的对象。 要求的第一个参数为 URI (统一资源定位符),通常就是服务器的 URL。 可选的第二个参数为传输工厂实例;在默认情况下对于 https: URL 是一个内部 SafeTransport 实例,在其他情况下则是一个内部 HTTP Transport 实例。 可选的第三个参数为编码格式,默认为 UTF-8。 可选的第四个参数为调试旗标。

The following parameters govern the use of the returned proxy instance. If allow_none is true, the Python constant None will be translated into XML; the default behaviour is for None to raise a TypeError. This is a commonly-used extension to the XML-RPC specification, but isn’t supported by all clients and servers; see http://ontosys.com/xml-rpc/extensions.php for a description. The use_builtin_types flag can be used to cause date/time values to be presented as datetime.datetime objects and binary data to be presented as bytes objects; this flag is false by default. datetime.datetime, bytes and bytearray objects may be passed to calls. The obsolete use_datetime flag is similar to use_builtin_types but it applies only to date/time values.

HTTP 和 HTTPS 传输均支持用于 HTTP 基本身份验证的 URL 语法扩展: http://user:pass@host:port/pathuser:pass 部分将以 base64 编码为 HTTP ‘Authorization’ 标头,并在发起调用 XML-RPC 方法时作为连接过程的一部分发送给远程服务器。 你只需要在远程服务器要求基本身份验证账号和密码时使用此语法。 如果提供了 HTTPS URL,context 可以为 ssl.SSLContext 并配置有下层 HTTPS 连接的 SSL 设置。

返回的实例是一个代理对象,具有可被用来在远程服务器上发起相应 RPC 调用的方法。 如果远程服务器支持内省 API,则也可使用该代理对象在远程服务器上查询它所支持的方法(服务发现)并获取其他服务器相关的元数据

适用的类型(即可通过 XML 生成 marshall 对象),包括如下类型(除了已说明的例外,它们会被反 marshall 为同样的 Python 类型):

XML-RPC类型

Python 类型

boolean

bool

int, i1, i2, i4, i8 或者 biginteger

int 的范围从 -2147483648 到 2147483647。值将获得 <int> 标志。

doublefloat

float. 值将获得 <double> 标志.

string

str

array

listtuple 包含整合元素。数组以 lists 形式返回。

struct

dict。 键必须为字符串,值可以为任何适用的类型。 可以传入用户自定义类的对象;只有其 __dict__ 属性会被传输。

dateTime.iso8601

DateTimedatetime.datetime。返回的类型取决于 use_builtin_typesuse_datetime 标志的值。

base64

Binary, bytesbytearray。返回的类型取决于 use_builtin_types 标志的值。

nil

None 常量。仅当 allow_none 为true时才允许传递。

bigdecimal

decimal.Decimal. 仅返回类型。

这是This is the full set of data types supported by XML-RPC 所支持数据类型的完整集合。 方法调用也可能引发一个特殊的 Fault 实例,用来提示 XML-RPC 服务器错误,或是用 ProtocolError 来提示 HTTP/HTTPS 传输层中的错误。 FaultProtocolError 都派生自名为 Error 的基类。 请注意 xmlrpc client 模块目前不可 marshal 内置类型的子类的实例。

当传入字符串时,XML 中的特殊字符如 <, >& 将被自动转义。 但是,调用方有责任确保字符串中没有 XML 中不允许的字符,例如 ASCII 值在 0 和 31 之间的控制字符(当然,制表、换行和回车除外);不这样做将导致 XML-RPC 请求的 XML 格式不正确。 如果你必须通过 XML-RPC 传入任意字节数据,请使用 bytesbytearray 类或者下文描述的 Binary 包装器类。

Server 被保留作为 ServerProxy 的别名用于向下兼容。 新的代码应当使用 ServerProxy

在 3.5 版更改: 增加了 context 参数。

在 3.6 版更改: 增加了对带有前缀的类型标签的支持 (例如 ex:nil)。 增加了对反 marshall 被 Apache XML-RPC 实现用于表示数值的附加类型的支持: i1, i2, i8, biginteger, floatbigdecimal。 请参阅 http://ws.apache.org/xmlrpc/types.html 了解详情。

参见

XML-RPC HOWTO

以多种语言对 XML-RPC 操作和客户端软件进行了很好的说明。 包含 XML-RPC 客户端开发者所需知道的几乎任何事情。

XML-RPC Introspection

描述了用于内省的 XML-RPC 协议扩展。

XML-RPC Specification

官方规范说明。

Unofficial XML-RPC Errata

Fredrik Lundh 的 “非官方勘误表,旨在澄清 XML-RPC 规范说明的某些细节,以及关于在设计你自己的 XML-RPC 实现时的 ‘最佳实践’ 的提示。”

21.26.1. ServerProxy 对象

ServerProxy 实例有一个方法与 XML-RPC 服务器所接受的每个远程过程调用相对应。 调用该方法会执行一个 RPC,通过名称和参数签名来调度(例如同一个方法名可通过多个参数签名来重载)。 RPC 结束时返回一个值,它可以是适用类型的返回数据或是表示错误的 FaultProtocolError 对象。

支持 XML 内省 API 的服务器还支持一些以保留的 system 属性分组的通用方法:

ServerProxy.system.listMethods()

此方法返回一个字符串列表,每个字符串都各自对应 XML-RPC 服务器所支持的(非系统)方法。

ServerProxy.system.methodSignature(name)

此方法接受一个形参,即某个由 XML-RPC 服务器所实现的方法名称。 它返回一个由此方法可能的签名组成的数组。 一个签名就是一个类型数组。 这些类型中的第一个是方法的的返回类型,其余的均为形参。

由于允许多个签名(即重载),此方法是返回一个签名列表而非一个单例。

签名本身被限制为一个方法所期望的最高层级形参。 举例来说如果一个方法期望有一个结构体数组作为形参,并返回一个字符串,则其签名就是 “string, array”。 如果它期望有三个整数并返回一个字符串,则其签名是 “string, int, int, int”。

如果方法没有定义任何签名,则将返回一个非数组值。 在 Python 中这意味着返回值的类型为列表以外的类型。

ServerProxy.system.methodHelp(name)

此方法接受一个形参,即 XML-RPC 服务器所实现的某个方法的名称。 它返回描述相应方法用法的文档字符串。 如果没有可用的文档字符串,则返回空字符串。 文档字符串可以包含 HTML 标记。

在 3.5 版更改: ServerProxy 的实例支持 context manager 协议用于关闭下层传输。

以下是一个可运行的示例。 服务器端代码:

from xmlrpc.server import SimpleXMLRPCServer

def is_even(n):
    return n % 2 == 0

server = SimpleXMLRPCServer(("localhost", 8000))
print("Listening on port 8000...")
server.register_function(is_even, "is_even")
server.serve_forever()

前述服务器的客户端代码:

import xmlrpc.client

with xmlrpc.client.ServerProxy("http://localhost:8000/") as proxy:
    print("3 is even: %s" % str(proxy.is_even(3)))
    print("100 is even: %s" % str(proxy.is_even(100)))

21.26.2. DateTime 对象

class xmlrpc.client.DateTime

该类的初始化可以使用距离 Unix 纪元的秒数、时间元组、ISO 8601 时间/日期字符串或 datetime.datetime 实例。 它具有下列方法,主要是为 marshall 和反 marshall 代码的内部使用提供支持:

decode(string)

接受一个字符串作为实例的新时间值。

encode(out)

将此 DateTime 条目的 XML-RPC 编码格式写入到 out 流对象。

它还通过富比较和 __repr__() 方法来支持某些 Python 内置操作。

以下是一个可运行的示例。 服务器端代码:

import datetime
from xmlrpc.server import SimpleXMLRPCServer
import xmlrpc.client

def today():
    today = datetime.datetime.today()
    return xmlrpc.client.DateTime(today)

server = SimpleXMLRPCServer(("localhost", 8000))
print("Listening on port 8000...")
server.register_function(today, "today")
server.serve_forever()

前述服务器的客户端代码:

import xmlrpc.client
import datetime

proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")

today = proxy.today()
# convert the ISO8601 string to a datetime object
converted = datetime.datetime.strptime(today.value, "%Y%m%dT%H:%M:%S")
print("Today: %s" % converted.strftime("%d.%m.%Y, %H:%M"))

21.26.3. Binary 对象

class xmlrpc.client.Binary

该类的初始化可以使用字节数据(可包括 NUL)。 对 Binary 对象的初始访问是由一个属性来提供的:

data

Binary 实例封装的二进制数据。 该数据以 bytes 对象的形式提供。

Binary 对象具有下列方法,支持这些方法主要是供 marshall 和反 marshall 代码在内部使用:

decode(bytes)

接受一个 base64 bytes 对象并将其解码为实例的新数据。

encode(out)

将此二进制条目的 XML-RPC base 64 编码格式写入到 out 流对象。

被编码数据将依据 RFC 2045 第 6.8 节 每 76 个字符换行一次,这是撰写 XML-RPC 规范说明时 base64 规范的事实标准。

它还通过 __eq__()__ne__() 方法来支持某些 Python 内置运算符。

该二进制对象的示例用法。 我们将通过 XMLRPC 来传输一张图片:

from xmlrpc.server import SimpleXMLRPCServer
import xmlrpc.client

def python_logo():
    with open("python_logo.jpg", "rb") as handle:
        return xmlrpc.client.Binary(handle.read())

server = SimpleXMLRPCServer(("localhost", 8000))
print("Listening on port 8000...")
server.register_function(python_logo, 'python_logo')

server.serve_forever()

客户端会获取图片并将其保存为一个文件:

import xmlrpc.client

proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")
with open("fetched_python_logo.jpg", "wb") as handle:
    handle.write(proxy.python_logo().data)

21.26.4. Fault 对象

class xmlrpc.client.Fault

Fault 对象封装了 XML-RPC fault 标签的内容。 Fault 对象具有下列属性:

faultCode

A string indicating the fault type.

faultString

一个包含与 fault 相关联的诊断消息的字符串。

在接下来的示例中我们将通过返回一个复数类型的值来故意引发一个 Fault。 服务器端代码:

from xmlrpc.server import SimpleXMLRPCServer

# A marshalling error is going to occur because we're returning a
# complex number
def add(x, y):
    return x+y+0j

server = SimpleXMLRPCServer(("localhost", 8000))
print("Listening on port 8000...")
server.register_function(add, 'add')

server.serve_forever()

前述服务器的客户端代码:

import xmlrpc.client

proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")
try:
    proxy.add(2, 5)
except xmlrpc.client.Fault as err:
    print("A fault occurred")
    print("Fault code: %d" % err.faultCode)
    print("Fault string: %s" % err.faultString)

21.26.5. ProtocolError 对象

class xmlrpc.client.ProtocolError

ProtocolError 对象描述了下层传输层中的协议错误(例如当 URI 所指定的服务器不存在时的 404 ‘not found’ 错误)。 它具有下列属性:

url

触发错误的 URI 或 URL。

errcode

错误代码。

errmsg

错误消息或诊断字符串。

headers

一个包含触发错误的 HTTP/HTTPS 请求的标头的字典。

在接下来的示例中我们将通过提供一个无效的 URI 来故意引发一个 ProtocolError:

import xmlrpc.client

# create a ServerProxy with a URI that doesn't respond to XMLRPC requests
proxy = xmlrpc.client.ServerProxy("http://google.com/")

try:
    proxy.some_method()
except xmlrpc.client.ProtocolError as err:
    print("A protocol error occurred")
    print("URL: %s" % err.url)
    print("HTTP/HTTPS headers: %s" % err.headers)
    print("Error code: %d" % err.errcode)
    print("Error message: %s" % err.errmsg)

21.26.6. MultiCall 对象

The MultiCall object provides a way to encapsulate multiple calls to a remote server into a single request 1.

class xmlrpc.client.MultiCall(server)

Create an object used to boxcar method calls. server is the eventual target of the call. Calls can be made to the result object, but they will immediately return None, and only store the call name and parameters in the MultiCall object. Calling the object itself causes all stored calls to be transmitted as a single system.multicall request. The result of this call is a generator; iterating over this generator yields the individual results.

A usage example of this class follows. The server code:

from xmlrpc.server import SimpleXMLRPCServer

def add(x, y):
    return x + y

def subtract(x, y):
    return x - y

def multiply(x, y):
    return x * y

def divide(x, y):
    return x // y

# A simple server with simple arithmetic functions
server = SimpleXMLRPCServer(("localhost", 8000))
print("Listening on port 8000...")
server.register_multicall_functions()
server.register_function(add, 'add')
server.register_function(subtract, 'subtract')
server.register_function(multiply, 'multiply')
server.register_function(divide, 'divide')
server.serve_forever()

前述服务器的客户端代码:

import xmlrpc.client

proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")
multicall = xmlrpc.client.MultiCall(proxy)
multicall.add(7, 3)
multicall.subtract(7, 3)
multicall.multiply(7, 3)
multicall.divide(7, 3)
result = multicall()

print("7+3=%d, 7-3=%d, 7*3=%d, 7//3=%d" % tuple(result))

21.26.7. Convenience Functions

xmlrpc.client.dumps(params, methodname=None, methodresponse=None, encoding=None, allow_none=False)

Convert params into an XML-RPC request. or into a response if methodresponse is true. params can be either a tuple of arguments or an instance of the Fault exception class. If methodresponse is true, only a single value can be returned, meaning that params must be of length 1. encoding, if supplied, is the encoding to use in the generated XML; the default is UTF-8. Python’s None value cannot be used in standard XML-RPC; to allow using it via an extension, provide a true value for allow_none.

xmlrpc.client.loads(data, use_datetime=False, use_builtin_types=False)

Convert an XML-RPC request or response into Python objects, a (params, methodname). params is a tuple of argument; methodname is a string, or None if no method name is present in the packet. If the XML-RPC packet represents a fault condition, this function will raise a Fault exception. The use_builtin_types flag can be used to cause date/time values to be presented as datetime.datetime objects and binary data to be presented as bytes objects; this flag is false by default.

The obsolete use_datetime flag is similar to use_builtin_types but it applies only to date/time values.

在 3.3 版更改: 增加了 use_builtin_types 旗标。

21.26.8. Example of Client Usage

# simple test program (from the XML-RPC specification)
from xmlrpc.client import ServerProxy, Error

# server = ServerProxy("http://localhost:8000") # local server
with ServerProxy("http://betty.userland.com") as proxy:

    print(proxy)

    try:
        print(proxy.examples.getStateName(41))
    except Error as v:
        print("ERROR", v)

To access an XML-RPC server through a HTTP proxy, you need to define a custom transport. The following example shows how:

import http.client
import xmlrpc.client

class ProxiedTransport(xmlrpc.client.Transport):

    def set_proxy(self, host, port=None, headers=None):
        self.proxy = host, port
        self.proxy_headers = headers

    def make_connection(self, host):
        connection = http.client.HTTPConnection(*self.proxy)
        connection.set_tunnel(host, headers=self.proxy_headers)
        self._connection = host, connection
        return connection

transport = ProxiedTransport()
transport.set_proxy('proxy-server', 8080)
server = xmlrpc.client.ServerProxy('http://betty.userland.com', transport=transport)
print(server.examples.getStateName(41))

21.26.9. 客户端与服务器用法的示例

参见 SimpleXMLRPCServer Example

备注

1

This approach has been first presented in a discussion on xmlrpc.com.