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實例,在其他情況下則是一個內部 HTTPTransport實例。 可選的第三個參數為編碼格式,默認為 UTF-8。 可選的第四個參數為調試旗標。The following parameters govern the use of the returned proxy instance. If allow_none is true, the Python constant
Nonewill be translated into XML; the default behaviour is forNoneto raise aTypeError. 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 asdatetime.datetimeobjects and binary data to be presented asbytesobjects; this flag is false by default.datetime.datetime,bytesandbytearrayobjects 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/path。user:pass部分將以 base64 編碼為 HTTP 'Authorization' 標頭,并在發起調用 XML-RPC 方法時作為連接過程的一部分發送給遠程服務器。 你只需要在遠程服務器要求基本身份驗證賬號和密碼時使用此語法。 如果提供了 HTTPS URL,context 可以為ssl.SSLContext并配置有下層 HTTPS 連接的 SSL 設置。返回的實例是一個代理對象,具有可被用來在遠程服務器上發起相應 RPC 調用的方法。 如果遠程服務器支持內省 API,則也可使用該代理對象在遠程服務器上查詢它所支持的方法(服務發現)并獲取其他服務器相關的元數據
適用的類型(即可通過 XML 生成 marshall 對象),包括如下類型(除了已說明的例外,它們會被反 marshall 為同樣的 Python 類型):
XML-RPC類型
Python 類型
booleanint,i1,i2,i4,i8或者bigintegerint的范圍從 -2147483648 到 2147483647。值將獲得<int>標志。double或floatfloat. 值將獲得<double>標志.stringarraystructdict。 鍵必須為字符串,值可以為任何適用的類型。 可以傳入用戶自定義類的對象;只有其__dict__屬性會被傳輸。dateTime.iso8601DateTime或datetime.datetime。返回的類型取決于 use_builtin_types 和 use_datetime 標志的值。base64nilNone常量。僅當 allow_none 為true時才允許傳遞。bigdecimaldecimal.Decimal. 僅返回類型。這是This is the full set of data types supported by XML-RPC 所支持數據類型的完整集合。 方法調用也可能引發一個特殊的
Fault實例,用來提示 XML-RPC 服務器錯誤,或是用ProtocolError來提示 HTTP/HTTPS 傳輸層中的錯誤。Fault和ProtocolError都派生自名為Error的基類。 請注意 xmlrpc client 模塊目前不可 marshal 內置類型的子類的實例。當傳入字符串時,XML 中的特殊字符如
<,>和&將被自動轉義。 但是,調用方有責任確保字符串中沒有 XML 中不允許的字符,例如 ASCII 值在 0 和 31 之間的控制字符(當然,制表、換行和回車除外);不這樣做將導致 XML-RPC 請求的 XML 格式不正確。 如果你必須通過 XML-RPC 傳入任意字節數據,請使用bytes或bytearray類或者下文描述的Binary包裝器類。Server被保留作為ServerProxy的別名用于向下兼容。 新的代碼應當使用ServerProxy。在 3.5 版更改: 增加了 context 參數。
在 3.6 版更改: 增加了對帶有前綴的類型標簽的支持 (例如
ex:nil)。 增加了對反 marshall 被 Apache XML-RPC 實現用于表示數值的附加類型的支持:i1,i2,i8,biginteger,float和bigdecimal。 請參閱 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 實現時的 '最佳實踐' 的提示。"
ServerProxy 對象?
ServerProxy 實例有一個方法與 XML-RPC 服務器所接受的每個遠程過程調用相對應。 調用該方法會執行一個 RPC,通過名稱和參數簽名來調度(例如同一個方法名可通過多個參數簽名來重載)。 RPC 結束時返回一個值,它可以是適用類型的返回數據或是表示錯誤的 Fault 或 ProtocolError 對象。
支持 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)))
DateTime 對象?
-
class
xmlrpc.client.DateTime? 該類的初始化可以使用距離 Unix 紀元的秒數、時間元組、ISO 8601 時間/日期字符串或
datetime.datetime實例。 它具有下列方法,主要是為 marshall 和反 marshall 代碼的內部使用提供支持:-
decode(string)? 接受一個字符串作為實例的新時間值。
它還通過富比較和
__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"))
Binary 對象?
-
class
xmlrpc.client.Binary? 該類的初始化可以使用字節數據(可包括 NUL)。 對
Binary對象的初始訪問是由一個屬性來提供的:Binary對象具有下列方法,支持這些方法主要是供 marshall 和反 marshall 代碼在內部使用:-
encode(out)? 將此二進制條目的 XML-RPC base 64 編碼格式寫入到 out 流對象。
被編碼數據將依據 RFC 2045 第 6.8 節 每 76 個字符換行一次,這是撰寫 XML-RPC 規范說明時 base64 規范的事實標準。
-
該二進制對象的示例用法。 我們將通過 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)
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)
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)
MultiCall 對象?
MultiCall 對象提供了一種將對遠程服務器的多個調用封裝為一個單獨請求的方式 1。
-
class
xmlrpc.client.MultiCall(server)? 創建一個用于盒式方法調用的對象。 server 是調用的最終目標。 可以對結果對象發起調用,但它們將立即返回
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))
便捷函數?
-
xmlrpc.client.dumps(params, methodname=None, methodresponse=None, encoding=None, allow_none=False)? 請 params 轉換為一個 XML-RPC 請求。 或者當 methodresponse 為真值時則轉換為一個請求。 params 可以是一個參數元組或是一個
Fault異常類的實例。 如果 methodresponse 為真值,只有單獨的值可以被返回,這意味著 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對象;此旗標默認為假值。已過時的 use_datetime 旗標與 use_builtin_types 類似但只作用于日期/時間值。
在 3.3 版更改: 增加了 use_builtin_types 旗標。
客戶端用法的示例?
# 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 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))
