SOAPでファイルを送信する方法を調べたところ、5つほど見つかりました。
参考:SOAP Attachments and Files | SoapUI
Zeepではどれが実装されているかを調べたところ、elementに直接送信ファイルを埋め込む形であるinlineはあるものの、他は実装されていなさそうでした。
ただ、MTOMについては、issueやPRがありました。
- Support for MTOM attachments · Issue #599 · mvantellingen/python-zeep
- SOAP MTOM Attachment · Issue #781 · mvantellingen/python-zeep
- Transport with attachment by ellethee · Pull Request #314 · mvantellingen/python-zeep
また、以下のcommitが取り込まれているため、将来的にはサポートされるかもしれません。
https://github.com/mvantellingen/python-zeep/commit/e346e91d6d8b0a37a84ab2bd9b423c25ddfd88f1#diff-40cce0d06cb2448df518c74200e882ae
そこで今回は、inline (Base64エンコード) でのファイル送信を試してみました。
なお、前提は以下です。
目次
環境
WSDLでの実装
inlineでデータを送信する場合、型(type)として xsd:base64Binary
を使った定義を用意します。
<!-- xmlns:xsd="http://www.w3.org/2001/XMLSchema" --> <xsd:element name="RequestInterface"> <xsd:complexType> <xsd:sequence> <xsd:element minOccurs="0" name="image" type="xsd:base64Binary" /> </xsd:sequence> </xsd:complexType> </xsd:element>
残りの部分は、以前作成したものと同じです。
Python + Zeep + SOAP UI + 自作WSDLで、SOAPのリクエストとレスポンスを試してみた - メモ的な思考的な
全体像はこんな感じです。
inline.wsdl
<?xml version="1.0" encoding="UTF-8"?> <wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap11="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns0="http://example.com/HelloWorld" targetNamespace="http://example.com/HelloWorld"> <wsdl:types> <xsd:schema elementFormDefault="qualified" targetNamespace="http://example.com/HelloWorld"> <xsd:element name="RequestInterface"> <xsd:complexType> <xsd:sequence> <xsd:element minOccurs="0" name="image" type="xsd:base64Binary" /> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="ResponseInterface"> <xsd:complexType> <xsd:sequence> <xsd:element minOccurs="0" name="returnMessage" type="xsd:string" /> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema> </wsdl:types> <wsdl:message name="messageIn"> <wsdl:part name="parameters" element="ns0:RequestInterface" /> </wsdl:message> <wsdl:message name="messageOut"> <wsdl:part name="parameters" element="ns0:ResponseInterface" /> </wsdl:message> <wsdl:portType name="HelloPort"> <wsdl:operation name="requestMessage"> <wsdl:input message="ns0:messageIn"/> <wsdl:output message="ns0:messageOut"/> </wsdl:operation> </wsdl:portType> <wsdl:binding name="InlineAttachmentBindingSoap11" type="ns0:HelloPort"> <soap11:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/> <wsdl:operation name="requestMessage"> <soap11:operation soapAction="http://example.com/HelloWorld/requestMessage" /> <wsdl:input> <soap11:body use="literal"/> </wsdl:input> <wsdl:output> <soap11:body use="literal"/> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="HelloService"> <wsdl:port name="InlineAttachmentServicePort" binding="ns0:InlineAttachmentBindingSoap11"> <soap11:address location="http://localhost:9400/inlineAttachmentBindingSoap11"/> </wsdl:port> </wsdl:service> </wsdl:definitions>
Zeepでの実装
Zeepでは、以下の流れで実装すれば良さそうです。
コードだとこんな感じです。
# バイナリ形式でファイルを読み込み with ATTACHMENT.open(mode='rb') as f: attachment_data = f.read() # ファイルをBase64エンコード encoded_data = base64.b64encode(attachment_data) client = Client(str(WSDL)) # ZeepクライアントにBase64エンコードしたデータを渡す # この場合、上記で定義した `image` elementに渡している response = client.service.requestMessage(image=encoded_data)
動作確認
SOAP UIにて動作確認します。
そこで、SOAP UIのMockServiceをSOAPサーバとして動作させます。
- SOAP UIにて、上記の
inline.wsdl
を指定してプロジェクトを作成します。 - エンドポイント
inlineAttachmentBindingSoap11
を右クリックし、Generate SOAP Mock Service
を選択 - Generate MockService ダイアログでは、以下を指定
- Generate SOAP MockService ダイアログでは、以下を指定
- Specify name of MockService to createは、デフォルト値
- MockServiceダイアログが表示されるため、実行ボタンを押す
- 右側に
run on port 9400
が表示されればOK
- 右側に
あとは、Zeepで作成したSOAPクライアントを実行します。
なお、送信時のSOAPエンベロープの内容を確認するため、History pluginを使います。
Python + Zeep + History pluginで、SOAPのリクエストとレスポンスを確認してみた - メモ的な思考的な
全体像は以下の通りです。
import base64 import pathlib from lxml import etree from zeep import Client from zeep.plugins import HistoryPlugin BASE_PATH = pathlib.Path(__file__).resolve().parents[0] WSDL = BASE_PATH.joinpath('inline.wsdl') ATTACHMENT = BASE_PATH.joinpath('shinanogold.png') def run(): with ATTACHMENT.open(mode='rb') as f: attachment_data = f.read() encoded_data = base64.b64encode(attachment_data) history_plugin = HistoryPlugin() client = Client(str(WSDL), plugins=[history_plugin]) response = client.service.requestMessage(image=encoded_data) print('--- request body ---') print(etree.tostring(history_plugin.last_sent['envelope'], pretty_print=True, encoding='unicode')) print('--- response ---') print(response) if __name__ == '__main__': run()
上記のPythonスクリプトを実行したところ、image要素に画像のBase64エンコードデータが含まれて送信されました(スペースの関係上、途中は省略しました)。
responseで ?
が返ってきていますが、SOAP UIで何も設定していないため、デフォルトのレスポンス値の ?
となっています。
$ python inline_runner.py --- request body --- <soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"> <soap-env:Body> <ns0:RequestInterface xmlns:ns0="http://example.com/HelloWorld"> <!-- Base64エンコードした画像データ(途中省略) --> <ns0:image>aVZ...PT0=</ns0:image> </ns0:RequestInterface> </soap-env:Body> </soap-env:Envelope> --- response --- ?
MockServiceダイアログを見ると、アクセスした時のログが追加されているため、ダブルクリックで表示します。
Request Messageタブを見ると、Zeepからリクエストした時のデータが表示されていました。
上記の内容を以下に転記しますが、History pluginの内容と一致しているため、うまく送信できたと考えられました。
<?xml version='1.0' encoding='utf-8'?> <soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"><soap-env:Body><ns0:RequestInterface xmlns:ns0="http://example.com/HelloWorld"><ns0:image>aVZ...PT0=</ns0:image></ns0:RequestInterface></soap-env:Body></soap-env:Envelope>
本当に一致しているかはBase64デコードして確認する必要がありますが、今回は省略します。
ソースコード
GitHubに上げました。 file_attachments/inline/
ディレクトリの中が今回のものです。
https://github.com/thinkAmi-sandbox/python_zeep-sample