Python + Zeep にて、SOAPの wsi:swaRef でファイルを送信する

前回、swaRef にて、SOAPでファイルを送信してみました。
Python + Zeep にて、SOAPのswaRef でファイルを送信する - メモ的な思考的な

 
今回は、wsi:swaRefという仕様でファイルを送信してみます。

 
なお、今回扱うwsi:swaRefについてですが、SOAPエンベロープは、

<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>cid:image=spam</ns0:image>
    </ns0:RequestInterface>
  </soap-env:Body>
</soap-env:Envelope>

のように <ns0:image>cid:image=spam</ns0:image> と、プレフィックス cid: を付与する形となります。

また、動作確認は SOAP UI を使っているため、もしかしたらそれ以外の環境では動作しないかもしれません。

 
目次

 

環境

 

実装するもの

です。

Transportについては、SwAの実装を流用できますので、今回は省略します(後述のGitHubにはTransportも実装してあります)。

 

WSDLの実装

今回は image elementの型として ref:swaRef を指定します。

そのため、

  • 名前空間 ref の追加
  • image elementの型を ref:swaRef と定義

をします。

 
ただ、名前空間を追加してZeepを実行すると

zeep.exceptions.NamespaceError: Unable to resolve type {http://ws-i.org/profiles/basic/1.1/xsd}swaRef. No schema available for the namespace 'http://ws-i.org/profiles/basic/1.1/xsd'.

というエラーが発生します。

 
試しにcurlを使って名前空間のページにアクセスしてみると

$ curl http://ws-i.org/profiles/basic/1.1/xsd -L
The page cannot be displayed because an internal server error has occurred.

とエラーになりました。

これにより、定義が見つからないために、Zeepがエラーを出していることが分かりました。

 
どこかに定義がないかを探してみると、仕様書の「4.4 Referencing Attachments from the SOAP Envelope」に記載がありました。

As a convenience, WS-I has published the schema for this schema type at: http://ws-i.org/profiles/basic/1.1/swaref.xsd

http://www.ws-i.org/Profiles/AttachmentsProfile-1.0-2004-08-24.html#Referencing_Attachments_from_the_SOAP_Envelope

 
指定されたURLを開くと、XMLスキーマがありました。

ただ、URLが微妙に異なっているため、WSDLの内容を差し替えてみました。

ただ、それでも同じエラーが発生しました。

No schema available for the namespace 'http://ws-i.org/profiles/basic/1.1/swaref.xsd'.

 
何か良い方法がないかを探したところ、スキーマを import する方法がありました。
XML Schemaのインポート

 
そこで、

$ curl http://ws-i.org/profiles/basic/1.1/swaref.xsd > wsi_swa_ref.xsd
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  4058  100  4058    0     0  11694      0 --:--:-- --:--:-- --:--:-- 11728

curlxmlwsi_swa_ref.xsd として取得します。

 
次に、取得したxsdファイルをimportします。

<wsdl:types>
    <xsd:schema>
        <xsd:import namespace="http://ws-i.org/profiles/basic/1.1/xsd"
                    schemaLocation="wsi_swa_ref.xsd" />
    </xsd:schema>
    <xsd:schema elementFormDefault="qualified" targetNamespace="http://example.com/HelloWorld">
    ...

 
これにより、型 ref:swaRef が使えるようになったため、型を差し替えました。

<xsd:element name="RequestInterface">
    <xsd:complexType>
        <xsd:sequence>
            <!-- WSI:swaRefのため、swaRef型の引数を用意 -->
            <xsd:element minOccurs="0" name="image" type="ref:swaRef" />
        </xsd:sequence>
    </xsd:complexType>
</xsd:element>

 

Zeepを実行するスクリプト

こちらも SwA のものを流用・修正します。

wsi:swaRefの場合、

  • SOAPエンベロープ部分の image 要素を <ns0:image>cid:image=ham</ns0:image> にする
  • 添付ファイル部分の Content-ID を Content-ID: <image=ham> にする

とするため、

def run(attachment_content_id, is_base64ize=False):
    session = Session()

    # WSI:swaRefの仕様書に合わせ、添付ファイルのContent-IDにプレフィックス 'image=' を追加
    attachment_content_id_with_prefix = f'image={attachment_content_id}'
    transport = WsiSwaRefTransport(ATTACHMENT,
                                   attachment_content_id=attachment_content_id_with_prefix,
                                   is_base64ize=is_base64ize, session=session)

    history_plugin = HistoryPlugin()
    client = Client(str(WSDL), transport=transport, plugins=[history_plugin])

    # WSI:swaRefの仕様書に合わせ、imageタグの値のプレフィックスに 'cid:' を追加
    response = client.service.requestMessage(image=f'cid:{attachment_content_id_with_prefix}')

という修正を加えました。

 

動作確認

SwAと同様、SOAP UIを使って動作を確認します。

SOAP UIをセットアップ後に実行すると、以下の結果となりました。 (量が多いため、バイナリのまま送信したもののみ記載)

$ python wsi_swa_ref_runner.py 
----------------------------------------
添付ファイルはバイナリのまま送信
----------------------------------------
b'--boundary_62604d9834ed41c7bdfbb5a1ea5f7cf7\r\n
Content-Type: text/xml; charset=utf-8\r\n
Content-Transfer-Encoding: 8bit\r\n
Content-ID: start_6b3ff1a1815c429786706f33495e4f25\r\n\r\n

<?xml version=\'1.0\' encoding=\'utf-8\'?>\n
<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>cid:image=ham</ns0:image>
    </ns0:RequestInterface>
  </soap-env:Body>
</soap-env:Envelope>\r\n
--boundary_62604d9834ed41c7bdfbb5a1ea5f7cf7\r\n
Content-Transfer-Encoding: binary\r\n
Content-Type: image/png; name="shinanogold.png"\r\n
Content-ID: <image=ham>\r\n
Content-Disposition: attachment; name="shinanogold.png"; filename="shinanogold.png"\r\n\r\n

\x89PNG...IEND\xaeB`\x82\r\n
--boundary_62604d9834ed41c7bdfbb5a1ea5f7cf7--'
--- history ---
{'envelope': <Element {http://schemas.xmlsoap.org/soap/envelope/}Envelope at 0x111cefec8>, 
 'http_headers': {
   'SOAPAction': '"http://example.com/HelloWorld/requestMessage"', 
   'Content-Type': 'multipart/related; boundary="boundary_62604d9834ed41c7bdfbb5a1ea5f7cf7";
                   type="text/xml"; start="start_6b3ff1a1815c429786706f33495e4f25"; charset=utf-8',
   'Content-Length': '6336'}}
?
--- envelope ---
<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>cid:image=ham</ns0:image>
    </ns0:RequestInterface>
  </soap-env:Body>
</soap-env:Envelope>

 
SOAP UIのログを見ると、2エントリが追加されていました。

内容は以下の通りです。

f:id:thinkAmi:20190102155653p:plain:w300

SOAP UIの説明では、wsi:swaRefだとTypeが SWAREF になるようですが、今回は MIME のままでした。
SOAP Attachments and Files | SoapUI

とはいえ、SOAP UI以外の環境がないため、今回はこれで良しとします。

 
また、ファイルをエクスポートしても、送信したファイル shinanogold.png を取得できました。

 

参考

ソースコード

GitHubに上げました。 file_attachments/wsi_swa_ref/ ディレクトリの中が今回のファイルです。
https://github.com/thinkAmi-sandbox/python_zeep-sample