> 目的:用于XML-RPC通信的客户端库。
XML-RPC是建立在HTTP和XML之上的轻量级远程过程调用协议。 ` xmlrpclib `模块允许Python程序与以任何语言编写的XML-RPC服务器进行通信。
本节中的所有示例均使用` xmlrpc_server.py `中定义的服务器,该服务器在源代码发行版中可用,并包含在此处以供参考。
xmlrpc_server.py
```
from xmlrpc.server import SimpleXMLRPCServer
from xmlrpc.client import Binary
import datetime
class ExampleService:
def ping(self):
"""Simple function to respond when called
to demonstrate connectivity.
"""
return True
def now(self):
"""Returns the server current date and time."""
return datetime.datetime.now()
def show_type(self, arg):
"""Illustrates how types are passed in and out of
server methods.
Accepts one argument of any type.
Returns a tuple with string representation of the value,
the name of the type, and the value itself.
"""
return (str(arg), str(type(arg)), arg)
def raises_exception(self, msg):
"Always raises a RuntimeError with the message passed in"
raise RuntimeError(msg)
def send_back_binary(self, bin):
"""Accepts single Binary argument, and unpacks and
repacks it to return it."""
data = bin.data
print('send_back_binary({!r})'.format(data))
response = Binary(data)
return response
if __name__ == '__main__':
server = SimpleXMLRPCServer(('localhost', 9000),
logRequests=True,
allow_none=True)
server.register_introspection_functions()
server.register_multicall_functions()
server.register_instance(ExampleService())
try:
print('Use Control-C to exit')
server.serve_forever()
except KeyboardInterrupt:
print('Exiting')
```
## 连接到服务器
将客户端连接到服务器的最简单方法是实例化` ServerProxy `对象,并为其提供服务器的URI。例如,演示服务器在本地主机的端口9000上运行。
xmlrpc_ServerProxy.py
```
import xmlrpc.client
server = xmlrpc.client.ServerProxy('http://localhost:9000')
print('Ping:', server.ping())
```
在这种情况下,服务的` ping()` 方法不带任何参数并返回单个布尔值。
```
$ python3 xmlrpc_ServerProxy.py
Ping: True
```
其他选项可用于支持替代运输。现成的同时支持 HTTP 和 HTTPS,并且都具有基本身份验证。为了实现新的通信通道,只需要一个新的传输类。例如,通过 SMTP 实现 XML-RPC 可能是一个有趣的练习。
xmlrpc_ServerProxy_verbose.py
```
import xmlrpc.client
server = xmlrpc.client.ServerProxy('http://localhost:9000',
verbose=True)
print('Ping:', server.ping())
```
` verbose `选项提供调试信息,可用于解决通信错误。
```
$ python3 xmlrpc_ServerProxy_verbose.py
send: b'POST /RPC2 HTTP/1.1..Host: localhost:9000..
Accept-Encoding: gzip..Content-Type: text/xml..
User-Agent: Python-xmlrpc/3.5..Content-Length: 98....'
send: b"..
ping...."
reply: 'HTTP/1.0 200 OK..'
header: Server header: Date header: Content-type header:
Content-length body: b"..
..1.
..."
Ping: True
```
如果需要备用系统,则可以从 UTF-8 更改默认编码。
xmlrpc_ServerProxy_encoding.py
```
import xmlrpc.client
server = xmlrpc.client.ServerProxy('http://localhost:9000',
encoding='ISO-8859-1')
print('Ping:', server.ping())
```
服务器自动检测正确的编码。
```
$ python3 xmlrpc_ServerProxy_encoding.py
Ping: True
```
` allow_none `选项控制Python的` None `值是否自动转换为nil值或是否导致错误。
xmlrpc_ServerProxy_allow_none.py
```
import xmlrpc.client
server = xmlrpc.client.ServerProxy('http://localhost:9000',
allow_none=False)
try:
server.show_type(None)
except TypeError as err:
print('ERROR:', err)
server = xmlrpc.client.ServerProxy('http://localhost:9000',
allow_none=True)
print('Allowed:', server.show_type(None))
```
如果客户端不允许` None `,则会在本地引发错误,但如果未将其配置为允许` None `,则也可以从服务器内部引发该错误。
```
$ python3 xmlrpc_ServerProxy_allow_none.py
ERROR: cannot marshal None unless allow_none is enabled
Allowed: ['None', "", None]
```
## 数据类型
XML-RPC 协议可识别一组有限的通用数据类型。这些类型可以作为参数或返回值传递,并可以组合以创建更复杂的数据结构。
xmlrpc_types.py
```
import xmlrpc.client
import datetime
server = xmlrpc.client.ServerProxy('http://localhost:9000')
data = [
('boolean', True),
('integer', 1),
('float', 2.5),
('string', 'some text'),
('datetime', datetime.datetime.now()),
('array', ['a', 'list']),
('array', ('a', 'tuple')),
('structure', {'a': 'dictionary'}),
]
for t, v in data:
as_string, type_name, value = server.show_type(v)
print('{:<12}: {}'.format(t, as_string))
print('{:12} {}'.format('', type_name))
print('{:12} {}'.format('', value))
```
简单类型是:
```
$ python3 xmlrpc_types.py
boolean : True
True
integer : 1
1
float : 2.5
2.5
string : some text
some text
datetime : 20160618T19:31:47
20160618T19:31:47
array : ['a', 'list']
['a', 'list']
array : ['a', 'tuple']
['a', 'tuple']
structure : {'a': 'dictionary'}
{'a': 'dictionary'}
```
可以嵌套支持的类型以创建任意复杂度的值。
xmlrpc_types_nested.py
```
import xmlrpc.client
import datetime
import pprint
server = xmlrpc.client.ServerProxy('http://localhost:9000')
data = {
'boolean': True,
'integer': 1,
'floating-point number': 2.5,
'string': 'some text',
'datetime': datetime.datetime.now(),
'array1': ['a', 'list'],
'array2': ('a', 'tuple'),
'structure': {'a': 'dictionary'},
}
arg = []
for i in range(3):
d = {}
d.update(data)
d['integer'] = i
arg.append(d)
print('Before:')
pprint.pprint(arg, width=40)
print('.After:')
pprint.pprint(server.show_type(arg)[-1], width=40)
```
该程序将包含所有受支持类型的词典列表传递到示例服务器,该服务器返回数据。元组将转换为列表,并且` datetime `实例将转换为` DateTime `对象,但否则数据将保持不变。
```
$ python3 xmlrpc_types_nested.py
Before:
[{'array': ('a', 'tuple'),
'boolean': True,
'datetime': datetime.datetime(2016, 6, 18, 19, 27, 30, 45333),
'floating-point number': 2.5,
'integer': 0,
'string': 'some text',
'structure': {'a': 'dictionary'}},
{'array': ('a', 'tuple'),
'boolean': True,
'datetime': datetime.datetime(2016, 6, 18, 19, 27, 30, 45333),
'floating-point number': 2.5,
'integer': 1,
'string': 'some text',
'structure': {'a': 'dictionary'}},
{'array': ('a', 'tuple'),
'boolean': True,
'datetime': datetime.datetime(2016, 6, 18, 19, 27, 30, 45333),
'floating-point number': 2.5,
'integer': 2,
'string': 'some text',
'structure': {'a': 'dictionary'}}]
After:
[{'array': ['a', 'tuple'],
'boolean': True,
'datetime': ,
'floating-point number': 2.5,
'integer': 0,
'string': 'some text',
'structure': {'a': 'dictionary'}},
{'array': ['a', 'tuple'],
'boolean': True,
'datetime': ,
'floating-point number': 2.5,
'integer': 1,
'string': 'some text',
'structure': {'a': 'dictionary'}},
{'array': ['a', 'tuple'],
'boolean': True,
'datetime': ,
'floating-point number': 2.5,
'integer': 2,
'string': 'some text',
'structure': {'a': 'dictionary'}}]
```
XML-RPC 支持将日期作为本机类型,并且` xmlrpclib `可以使用两个类之一来表示传出代理中或从服务器接收到的日期值。
xmlrpc_ServerProxy_use_datetime.py
```
import xmlrpc.client
server = xmlrpc.client.ServerProxy('http://localhost:9000',
use_datetime=True)
now = server.now()
print('With:', now, type(now), now.__class__.__name__)
server = xmlrpc.client.ServerProxy('http://localhost:9000',
use_datetime=False)
now = server.now()
print('Without:', now, type(now), now.__class__.__name__)
```
默认情况下,使用内部版本的` DateTime `,但是` use_datetime `选项打开支持使用[`datetime`](https://pymotw.com/3/datetime/index.html#module-datetime "datetime: Date and time value manipulation.")模块。
```
$ python3 source/xmlrpc.client/xmlrpc_ServerProxy_use_datetime.py
With: 2016-06-18 19:18:31 datetime
Without: 20160618T19:18:31 DateTime
```
## 传递对象
Python 类的实例被视为结构,并作为字典传递,而对象的属性作为字典中的值。
xmlrpc_types_object.py
```
import xmlrpc.client
import pprint
class MyObj:
def __init__(self, a, b):
self.a = a
self.b = b
def __repr__(self):
return 'MyObj({!r}, {!r})'.format(self.a, self.b)
server = xmlrpc.client.ServerProxy('http://localhost:9000')
o = MyObj(1, 'b goes here')
print('o :', o)
pprint.pprint(server.show_type(o))
o2 = MyObj(2, o)
print('.o2 :', o2)
pprint.pprint(server.show_type(o2))
```
## 传递对象
Python 类的实例被视为结构,并作为字典传递,而对象的属性作为字典中的值。
xmlrpc_types_object.py
```
导入xmlrpc.client
导入pprint
MyObj类:
def __init __(self,a,b):
self.a = a
self.b = b
def __repr __(自己):
返回'MyObj({!r},{!r})'。format(self.a,self.b)
服务器= xmlrpc.client.ServerProxy('http:/// localhost:9000')
o = MyObj(1,'b去这里')
打印('o:',o)
pprint.pprint(server.show_type(o))
o2 = MyObj(2,o)
print('。o2:',o2)
pprint.pprint(server.show_type(o2))
```
当该值从服务器发送回客户端时,结果是客户端上的字典,因为值中没有任何编码来告诉服务器(或客户端)应将其实例化为类的一部分。
```
$ python3 xmlrpc_types_object.py
o : MyObj(1, 'b goes here')
["{'b': 'b goes here', 'a': 1}", "",
{'a': 1, 'b': 'b goes here'}]
o2 : MyObj(2, MyObj(1, 'b goes here'))
["{'b': {'b': 'b goes here', 'a': 1}, 'a': 2}",
"",
{'a': 2, 'b': {'a': 1, 'b': 'b goes here'}}]
```
## 二进制数据
传递给服务器的所有值都将被编码并自动转义。但是,某些数据类型可能包含无效的 XML 字符。例如,二进制图像数据可能包括 ASCII 控制范围为 0 到 31 的字节值。要传递二进制数据,最好使用` Binary `类对其进行编码以进行传输。
xmlrpc_Binary.py
```
import xmlrpc.client
import xml.parsers.expat
server = xmlrpc.client.ServerProxy('http://localhost:9000')
s = b'This is a string with control characters.00'
print('Local string:', s)
data = xmlrpc.client.Binary(s)
response = server.send_back_binary(data)
print('As binary:', response.data)
try:
print('As string:', server.show_type(s))
except xml.parsers.expat.ExpatError as err:
print('.ERROR:', err)
```
如果将包含 NULL 字节的字符串传递给` show_type()`,则 XML 解析器在处理响应时会引发异常。
```
$ python3 xmlrpc_Binary.py
Local string: b'This is a string with control characters.00'
As binary: b'This is a string with control characters.00'
ERROR: not well-formed (invalid token): line 6, column 55
```
`Binary` 对象还可以用于通过 [`pickle`](https://pymotw.com/3/pickle/index.html#module-pickle "pickle: Object serialization")。与通过网络发送多少可执行代码有关的常规安全问题在这里适用(即除非通信通道安全,否则不要这样做)。
```
import xmlrpc.client
import pickle
import pprint
class MyObj:
def __init__(self, a, b):
self.a = a
self.b = b
def __repr__(self):
return 'MyObj({!r}, {!r})'.format(self.a, self.b)
server = xmlrpc.client.ServerProxy('http://localhost:9000')
o = MyObj(1, 'b goes here')
print('Local:', id(o))
print(o)
print('.As object:')
pprint.pprint(server.show_type(o))
p = pickle.dumps(o)
b = xmlrpc.client.Binary(p)
r = server.send_back_binary(b)
o2 = pickle.loads(r.data)
print('.From pickle:', id(o2))
pprint.pprint(o2)
```
` Binary `实例的data属性包含该对象的 Pickled 版本,因此必须先对其进行 Pickled 才能使用它。这导致另一个对象(具有新的id值)。
```x
$ python3 xmlrpc_Binary_pickle.py
Local: 4327262304
MyObj(1, 'b goes here')
As object:
["{'a': 1, 'b': 'b goes here'}", "",
{'a': 1, 'b': 'b goes here'}]
From pickle: 4327262472
MyObj(1, 'b goes here')
```
## 异常处理
由于 XML-RPC 服务器可以用任何语言编写,因此无法直接传输异常类。相反,服务器中引发的异常将转换为` Fault `对象,并在客户端本地作为异常引发。
xmlrpc_exception.py
```
import xmlrpc.client
server = xmlrpc.client.ServerProxy('http://localhost:9000')
try:
server.raises_exception('A message')
except Exception as err:
print('Fault code:', err.faultCode)
print('Message :', err.faultString)
```
原始错误消息保存在` faultString `属性中,并且` faultCode `设置为 XML-RPC 错误号。
```
$ python3 xmlrpc_exception.py
Fault code: 1
Message : :A message
```
## 将呼叫合并为一条消息
多重调用是 XML-RPC 协议的扩展,它允许同时发送多个调用,并收集响应并将其返回给调用者。
xmlrpc_MultiCall.py
```
import xmlrpc.client
server = xmlrpc.client.ServerProxy('http://localhost:9000')
multicall = xmlrpc.client.MultiCall(server)
multicall.ping()
multicall.show_type(1)
multicall.show_type('string')
for i, r in enumerate(multicall()):
print(i, r)
```
要使用` MultiCall `实例,请像使用` ServerProxy `一样调用其上的方法,然后调用不带参数的对象以实际运行远程功能。返回值是一个迭代器,它从所有调用中产生结果。
```
$ python3 xmlrpc_MultiCall.py
0 True
1 ['1', "", 1]
2 ['string', "", 'string']
```
如果其中一个调用导致` Fault `,则当从迭代器生成结果并且没有更多结果可用时,将引发异常。
xmlrpc_MultiCall_exception.py
```
import xmlrpc.client
server = xmlrpc.client.ServerProxy('http://localhost:9000')
multicall = xmlrpc.client.MultiCall(server)
multicall.ping()
multicall.show_type(1)
multicall.raises_exception('Next to last call stops execution')
multicall.show_type('string')
try:
for i, r in enumerate(multicall()):
print(i, r)
except xmlrpc.client.Fault as err:
print('ERROR:', err)
```
由于来自` raises_exception() `的第三个响应会生成异常,因此无法访问来自` show_type() `的响应。
```
$ python3 xmlrpc_MultiCall_exception.py
0 True
1 ['1', "", 1]
ERROR: :Next to last call stops execution">
```
也可以看看
- [Standard library documentation for xmlrpc.client](https://docs.python.org/3.7/library/xmlrpc.client.html)
- [` xmlrpc.server `](https://pymotw.com/3/xmlrpc.server/index.html#module-xmlrpc.server "xmlrpc.server: Implements an XML-RPC server.") – XML-RPC服务器实现。
- [`http.server`](https://pymotw.com/3/http.server/index.html#module-http.server "http.server: Base classes for implementing web servers.") – HTTP服务器实施。
- [XML-RPC How To](http://www.tldp.org/HOWTO/XML-RPC-HOWTO/index.html) – 介绍如何使用XML-RPC来以多种语言实现客户端和服务器。