13.11. xmlrpc.client — XML-RPC 客户端类库
目的:用于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"<?xml version='1.0'?>.<methodCall>.<methodName>
ping</methodName>.<params>.</params>.</methodCall>."
reply: 'HTTP/1.0 200 OK..'
header: Server header: Date header: Content-type header:
Content-length body: b"<?xml version='1.0'?>.<methodResponse>.
<params>.<param>.<value><boolean>1</boolean></value>.</param>
.</params>.</methodResponse>."
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', "<class 'NoneType'>", 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
<class 'bool'>
True
integer : 1
<class 'int'>
1
float : 2.5
<class 'float'>
2.5
string : some text
<class 'str'>
some text
datetime : 20160618T19:31:47
<class 'xmlrpc.client.DateTime'>
20160618T19:31:47
array : ['a', 'list']
<class 'list'>
['a', 'list']
array : ['a', 'tuple']
<class 'list'>
['a', 'tuple']
structure : {'a': 'dictionary'}
<class 'dict'>
{'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': <DateTime '20160618T19:27:30' at 0x101ecfac8>,
'floating-point number': 2.5,
'integer': 0,
'string': 'some text',
'structure': {'a': 'dictionary'}},
{'array': ['a', 'tuple'],
'boolean': True,
'datetime': <DateTime '20160618T19:27:30' at 0x101ecfcc0>,
'floating-point number': 2.5,
'integer': 1,
'string': 'some text',
'structure': {'a': 'dictionary'}},
{'array': ['a', 'tuple'],
'boolean': True,
'datetime': <DateTime '20160618T19:27:30' at 0x101ecfe10>,
'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
模块。
$ python3 source/xmlrpc.client/xmlrpc_ServerProxy_use_datetime.py
With: 2016-06-18 19:18:31 <class 'datetime.datetime'> datetime
Without: 20160618T19:18:31 <class 'xmlrpc.client.DateTime'> 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}", "<class 'dict'>",
{'a': 1, 'b': 'b goes here'}]
o2 : MyObj(2, MyObj(1, 'b goes here'))
["{'b': {'b': 'b goes here', 'a': 1}, 'a': 2}",
"<class 'dict'>",
{'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
。与通过网络发送多少可执行代码有关的常规安全问题在这里适用(即除非通信通道安全,否则不要这样做)。
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值)。
$ python3 xmlrpc_Binary_pickle.py
Local: 4327262304
MyObj(1, 'b goes here')
As object:
["{'a': 1, 'b': 'b goes here'}", "<class 'dict'>",
{'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 : <class 'RuntimeError'>: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', "<class 'int'>", 1]
2 ['string', "<class 'str'>", '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', "<class 'int'>", 1]
ERROR: <Fault 1: "<class 'RuntimeError'>:Next to last call stops execution">
也可以看看
- Standard library documentation for xmlrpc....
xmlrpc.server
– XML-RPC服务器实现。http.server
– HTTP服务器实施。- XML-RPC How To – 介绍如何使用XML-RPC来以多种语言实现客户端和服务器。
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。