# 21.26.`xmlrpc.client`-- XML_RPC客户端进程

源代码： Lib / xmlrpc / client.py

XML-RPC是一种使用通过HTTP传递信息的XML作为传输工具的远程过程调用方法。通过使用这个模块，客户端可以调用远程服务器上的参数（服务器由URI命名）并返回结构化数据的方法。此模块支持编写XML-RPC客户端代码; 它还可以处理所有符合Python对象特点的项目和XML之间转换的细节。

警告

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

在 3.5版本中的变化:For HTTPS URIs, `xmlrpc.client` 现在默认的进行验证和hostname检查

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版本中的变化:The use_builtin_types 被添加.

  `ServerProxy`实例是管理与远程XML-RPC服务器通信的对象。所需的第一个参数是URI（统一资源指示符），通常是服务器的URL。可选的第二个参数是传输协议实例;默认情况下，它是https：URL的内部`SafeTransport` 实例，否则为内部其他的HTTP传输`Transport` 实例可选的第三个参数是编码方式，默认为UTF-8（UTF-8（8-bit Unicode Transformation Format）是一种针对Unicode的可变长度字符编码，又称万国码）。可选的第四个参数是调试标志。

  以下参数控制返回的代理服务器实例的使用。allow_none 是true，Python常量`None`将被转换为XML;此时的python默认行为是由于`None`而抛出的`TypeError`.这是对XML-RPC规范的常用扩展，但不是所有的客户端和服务器都支持；有关说明，请参阅http://ontosys.com/xml-rpc/extensions.php。use_builtin_types标志可用于使日期/时间值显示为`datetime.datetime`的对象，二进制数据显示为`bytes`对象；默认情况下此标志为false。`datetime.datetime`，`bytes`和`bytearray`对象可以传递给调用。过时的use_datetime标志与use_builtin_types类似，但它仅适用于日期/时间值。

  HTTP和HTTPS传输均支持HTTP基本身份验证的URL语法扩展：`http：// user：pass @ host：port / path`。`user：pass`部分将被base64编码为HTTP“授权”头文件，并在调用XML-RPC方法时作为连接过程的一部分发送到远程服务器。如果远程服务器需要基本身份验证用户和密码，则只需使用此选项。如果提供了HTTPS网址，则context可能是`ssl.SSLContext`并配置底层HTTPS连接的SSL设置。

  返回的实例是一个代理对象，其方法可用于调用远程服务器上的相应RPC调用。如果远程服务器支持自检API，则代理还可以用于向远程服务器查询其支持的方法（服务发现）和获取其他与服务器关联的元数据。

  符合的类型（例如可以通过XML编组），包括以下（除非另有说明，它们被解组为同一个Python类型）：

  XML-RPC类型 | Python类型
  --- | ---
  `boolean` | `bool`
  `int`或`i4` | `int`，范围从-2147483648到2147483647。
  `double` | `float`
  `string` | `str`
  `array` | `list`或`tuple`。数组以`lists`返回。
  `struct` | `dict`。键必须是字符串，值可以是任何合适的类型。可以传递用户定义类的对象；只传输其`__ dict __`属性。
  `dateTime.iso8601` | `DateTime`或`datetime.datetime`。返回类型取决于use_builtin_types和use_datetime两个数据的值。
  `base64` | `Binary`，`bytes`或`bytearray`。返回类型取决于use_builtin_types数据的值。
  `nil` | `None`常数。仅当allow_none为true时，才允许传递。

  这是XML-RPC支持的全套数据类型。方法调用也可以引发用于发信号通知XML-RPC服务器错误的特殊`Fault`实例，或者用于在HTTP / HTTPS传输层中用信号通知错误的`ProtocolError`。`故障`和`ProtocolError`都源自一个名为`错误`的基类。请注意，xmlrpc客户端模块当前不封装并且发送内置类型的子类的实例。

  当传递字符串时，服务器会自动的避开`<`, `>`,和`&`中所带的字符串但是，调用者有责任确保字符串中没有XML中不允许使用的字符，例如ASCII值在0到31之间的控制字符（当然是tab，换行符和回车符除外）。如果含有违禁字符，将导致XML-RPC请求不到格式良好的XML。如果您必须通过XML-RPC传递任意字节，请使用`bytes`或`bytearray`类或下面描述的`Binary`包装类。

  `Server`作为`ServerProxy`的别名保留，以实现向后的兼容性。新代码应使用`ServerProxy`。

  在版本3.5中已更改：添加了Context参数。

也可以看看

XML-RPC HOWTO
  可以在几种语言中很好的描述XML-RPC文件的参数。包含XML-RPC客户端开发人员需要知道的几乎所有内容。
XML-RPC内省
  描述用于内省的XML-RPC协议扩展。
XML-RPC规范
  官方规格。
非正式XML-RPC错误
  Fredrik Lundh的“非官方勘误，旨在澄清XML-RPC规范中的某些细节，以及设计在自己的XML-RPC实现时使用”最佳实践“的提示。

## 21.26.1. 服务器项目

`ServerProxy`实例具有与XML-RPC服务器接受的每个远程过程调用相对应的方法。调用方法执行RPC，通过名称和参数签名（例如，相同的方法名称可以重载多个参数签名）。RPC通过返回一个值完成这一过程，该值可以是符合类型的返回数据，或者是指示错误的`Fault`或`ProtocolError`对象。

支持XML自省API的服务器支持在保留的`系统`属性下分组的一些常用方法：

`ServerProxy.system.``listMethods`()

  此方法返回一个字符串列表，一个每个XML-RPC服务器都支持的（非系统）方法。

`ServerProxy.system.``methodSignature`(name)

  此方法接受一个参数，即XML-RPC服务器实现的方法的名称。它返回此方法的可能的签名所组成的数组。签名是一个类型数组。第一种类型是方法的返回类型，其余的都是参数。

  因为多个签名（即重载）是允许的，所以此方法返回签名列表而不是单例。

  签名本身仅限于方法预期的顶级参数。对于实例，如果一个方法期望一个结构体数组作为参数，并返回一个字符串，那么它的签名只是“string，array”。如果它期望三个整数并返回一个字符串，它的签名是“string，int，int，int”。

  如果没有为方法定义签名，则返回非数组值。在Python中，这意味着返回值的类型将不是list。

`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 Objects

class `xmlrpc.client.``DateTime`

  此类可以从时代开始以秒为单位初始化，时间元组，ISO 8601时间/日期字符串或`datetime.datetime`实例。它有以下方法，主要支持由编组/取消编组代码内部使用：

  `decode`(string)

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

  `encode`(out)

    将此`DateTime`项的XML-RPC编码写入输出流对象。

  它还通过丰富的比较和`__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 Objects

class `xmlrpc.client.``Binary`

  该类可以从字节数据（其可以包括NUL）初始化。对`Binary`对象的内容的主访问由属性提供：

  `data`

    由`Binary`实例封装的二进制数据。数据作为`bytes`对象提供。

  `Binary`对象具有以下方法，主要由编组/取消编组代码内部使用：

  `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 Objects

class `xmlrpc.client.``Fault`

  `Fault`对象封装了XML-RPC故障标记的内容。故障对象具有以下属性：

  `faultCode`

    指示故障类型的字符串。

  `faultString`

    包含与故障相关联的诊断消息的字符串。

在下面的示例中，我们将通过返回一个复杂类型对象来故意造成`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 Objects

class `xmlrpc.client.``ProtocolError`

  `ProtocolError`对象描述底层传输层中的协议错误（例如，如果由URI命名的服务器不存在，则404“未找到”错误）。它具有以下属性：

  `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` 对象提供了一种将对远程服务器的多个调用封装到单个请求中的方法 [1].

class `xmlrpc.client.``MultiCall`(server)

  创建一个用于boxcar方法调用的对象。服务器 是最终的调用目标可以对结果对象进行调用，但是它们将立即返回 `None`,并且仅将调用名称和参数存储在 `MultiCall` 对象中。调用对象本身会导致所有存储的调用作为单个`system.multicall`请求传输。这个调用的结果是一个generator迭代器; 迭代该生成器产生单独的结果。

下面是一个可用的例子服务器代码：

    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. 便利功能

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

  将params转换为XML-RPC请求。或者如果方法响应为真，则返回响应。params可以是参数的一个元组，也可以是`Fault`异常类的实例。如果methodresponse为true，则只能返回一个值，这意味着params必须为长度1. encoding编码用于生成的XML；默认值为UTF-8。Python的`None`值不能在标准XML-RPC中使用；以允许通过扩展使用它，为allow_none提供真实值。

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

  将XML-RPC请求或响应转换为Python对象`（params， methodname）`。params是参数的元组； methodname是字符串，或`None`如果数据包中没有方法名称。如果XML-RPC数据包表示故障条件，则此函数将引发`Fault`异常。use_builtin_types标志可用于使日期/时间值显示为`datetime.datetime`对象，二进制数据显示为`bytes`对象；默认情况下此标志为false。

  过时的use_datetime标志与use_builtin_types类似，但它仅适用于日期/时间值。

  在版本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)

要通过HTTP代理访问XML-RPC服务器，您需要定义自定义传输。以下示例显示如何：

    import xmlrpc.client, http.client

    class ProxiedTransport(xmlrpc.client.Transport):
        def set_proxy(self, proxy):
            self.proxy = proxy

        def make_connection(self, host):
            self.realhost = host
            h = http.client.HTTPConnection(self.proxy)
            return h

        def send_request(self, connection, handler, request_body, debug):
            connection.putrequest("POST", 'http://%s%s' % (self.realhost, handler))

        def send_host(self, connection, host):
            connection.putheader('Host', self.realhost)

    p = ProxiedTransport()
    p.set_proxy('proxy-server:8080')
    server = xmlrpc.client.ServerProxy('http://time.xmlrpc.com/RPC2', transport=p)
    print(server.currentTime.getCurrentTime())

## 21.26.9. Example of Client and Server Usage

请参见SimpleXMLRPCServer Example。

脚注

[1] | 这种方法已经在关于xmlrpc.com的讨论中首次提出。