SOAP APIの基本を理解する：実装から活用まで

RESTが主流の現在でも金融・医療などの分野ではSOAP APIが活用されています。

・WSDLによる契約ベースの通信
・XMLメッセージの構造
・Pythonでの実装方法(Zeep/Spyne)

自分用の備忘録的としてエンタープライズ開発に役立つ知識をまとめてみました！✨

## はじめに

Webサービスの実装方法の一つとして、SOAP（Simple Object Access Protocol）は長年にわたり企業システムで広く採用されてきました。RESTが人気を集める現在でも、特に金融、医療、通信など多くのエンタープライズシステムではSOAP APIが活用されています。この記事では、SOAP APIの基本概念から実装方法、XMLとWSDLの構造、そして実際の活用シーンまでを初心者にも分かりやすく解説します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/5ede2bfb-3bd6-4eae-bcfc-4a814048ac0d.png)

## 目次
1. SOAP APIとは
2. SOAPの基本構造とXML
3. WSDLとは何か、その役割と構造
4. SOAP通信の実装方法
5. 実際の実装例
6. SOAPとRESTの比較
7. セキュリティ上の注意点
8. まとめ

## 1. SOAP APIとは

SOAP（Simple Object Access Protocol）は、構造化されたデータをネットワーク上で交換するためのプロトコルです。主に以下の特徴があります：

- XMLベースのメッセージング規格
- 様々なトランスポートプロトコル（HTTP、SMTP、TCPなど）上で動作可能
- 厳格な型定義と契約ベースの通信
- プラットフォームやプログラミング言語に依存しない相互運用性
- エンタープライズレベルのセキュリティ機能

SOAPは主にB2B（企業間）の通信や、厳格なトランザクション処理が必要なシステムで利用されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/1953ffd6-bc78-4087-8498-04dc63fbccb7.png)


### SOAPとRESTの違い

上図のように、SOAPとRESTには明確な違いがあります。SOAPはプロトコルであり、RESTはアーキテクチャスタイルです。SOAPは厳格な規約に基づいたメッセージングであるのに対し、RESTはよりシンプルでリソース指向のアプローチを取ります。

### SOAPエンベロープの構造

SOAP通信の基本となるのはXMLフォーマットのSOAPエンベロープです。以下の図はSOAPエンベロープの基本構造を示しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/0bacce5d-199f-4260-b749-08ac160c653e.png)

SOAPエンベロープは主に以下の要素から構成されています：

1. **SOAPエンベロープ（Envelope）**: メッセージ全体を囲む最上位要素
2. **SOAPヘッダー（Header）**: オプションの要素で認証情報などのメタデータを含む
3. **SOAPボディ（Body）**: 実際のデータやメソッド呼び出し情報を含む
4. **SOAPフォルト（Fault）**: エラー発生時のエラー情報を格納する要素

## 2. SOAPの基本構造とXML

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/3c3a127b-bda0-41f6-b118-8a040dd5e678.png)

### SOAPメッセージの基本構造

SOAPメッセージはXML形式で構造化され、以下の要素から構成されています：

1. **SOAPエンベロープ（Envelope）**: メッセージ全体を囲む最上位要素
2. **SOAPヘッダー（Header）**: オプションの要素で認証情報などのメタデータを含む
3. **SOAPボディ（Body）**: 実際のデータやメソッド呼び出し情報を含む
4. **SOAPフォルト（Fault）**: エラー発生時のエラー情報を格納する要素

基本的なSOAPメッセージの例：

```xml
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope 
    xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    
    <soap:Header>
        <auth:Credentials xmlns:auth="http://example.com/auth">
            <auth:Username>user123</auth:Username>
            <auth:Password>pass456</auth:Password>
        </auth:Credentials>
    </soap:Header>
    
    <soap:Body>
        <m:GetStockPrice xmlns:m="http://example.com/stock">
            <m:StockName>GOOG</m:StockName>
        </m:GetStockPrice>
    </soap:Body>
    
</soap:Envelope>
```

### SOAPエンコーディングルール

SOAPではXMLスキーマ（XSD）を使用して、データ型やメッセージ構造を厳密に定義します。主なエンコーディングルールとしては：

- 基本データ型（文字列、整数、日付など）の表現方法
- 複合型の構造（配列、オブジェクトなど）
- 名前空間による要素の識別
- NULL値や特殊文字の扱い

## 3. WSDLとは何か、その役割と構造

![wsdl-structure.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/2d93be17-0807-465b-ab9e-21ae2dd5daa1.png)

### WSDLの役割

WSDL（Web Services Description Language）は、Webサービスを記述するためのXMLベースの言語です。WSDLファイルは「サービスの契約書」として機能し、以下の情報を提供します：

- サービスが提供する操作（メソッド）
- 入力と出力のメッセージ形式
- データ型の定義
- バインディング情報（使用するプロトコルや通信方式）
- サービスのエンドポイント（URL）

WSDLによって、クライアントはサービスとどのように通信すべきかを正確に理解できます。多くの開発ツールはWSDLファイルから自動的にクライアントコードを生成する機能を提供しています。

### WSDLの基本構造

WSDLドキュメントは以下の主要なセクションから構成されています：

1. **types**: XMLスキーマを使用して定義されるデータ型
2. **message**: 送受信されるメッセージの形式（パラメータと戻り値）
3. **portType**: サービスが提供する操作（メソッド）の抽象的な定義
4. **binding**: 具体的なプロトコルとデータ形式の指定
5. **service**: サービスのエンドポイント（URL）の定義

基本的なWSDLの例：

```xml
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="StockService"
    targetNamespace="http://example.com/stock.wsdl"
    xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:tns="http://example.com/stock.wsdl"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">

    <!-- データ型の定義 -->
    <types>
        <xsd:schema targetNamespace="http://example.com/stock.xsd">
            <xsd:element name="GetStockPriceRequest">
                <xsd:complexType>
                    <xsd:sequence>
                        <xsd:element name="StockName" type="xsd:string"/>
                    </xsd:sequence>
                </xsd:complexType>
            </xsd:element>
            <xsd:element name="GetStockPriceResponse">
                <xsd:complexType>
                    <xsd:sequence>
                        <xsd:element name="Price" type="xsd:decimal"/>
                    </xsd:sequence>
                </xsd:complexType>
            </xsd:element>
        </xsd:schema>
    </types>

    <!-- メッセージの定義 -->
    <message name="GetStockPriceInput">
        <part name="parameters" element="tns:GetStockPriceRequest"/>
    </message>
    <message name="GetStockPriceOutput">
        <part name="parameters" element="tns:GetStockPriceResponse"/>
    </message>

    <!-- ポートタイプ（インターフェース）の定義 -->
    <portType name="StockServicePortType">
        <operation name="GetStockPrice">
            <input message="tns:GetStockPriceInput"/>
            <output message="tns:GetStockPriceOutput"/>
        </operation>
    </portType>

    <!-- バインディングの定義（SOAP/HTTPの使用） -->
    <binding name="StockServiceSOAP" type="tns:StockServicePortType">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <operation name="GetStockPrice">
            <soap:operation soapAction="http://example.com/GetStockPrice"/>
            <input>
                <soap:body use="literal"/>
            </input>
            <output>
                <soap:body use="literal"/>
            </output>
        </operation>
    </binding>

    <!-- サービスエンドポイントの定義 -->
    <service name="StockService">
        <port name="StockServicePort" binding="tns:StockServiceSOAP">
            <soap:address location="http://example.com/stockservice"/>
        </port>
    </service>
</definitions>
```

### WSDLバージョン

WSDLには主に2つのバージョンがあります：

- **WSDL 1.1**: 最も広く使われているバージョン
- **WSDL 2.0**: より最新の規格だが、1.1ほど普及していない

## 4. SOAP通信の実装方法

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/7e0a5b17-a81a-4f3e-b0b5-1cfdfefac072.png)

### クライアント側の実装 - Zeepライブラリ

Pythonで最も一般的なSOAPクライアントライブラリは「Zeep」です。最新かつ活発に開発されており、高性能で使いやすいAPIを提供しています。

```python
from zeep import Client
from zeep.transports import Transport
from requests import Session
import logging

# デバッグログの設定（オプション）
logging.basicConfig(level=logging.DEBUG)

# セッションの作成とカスタマイズ（認証など）
session = Session()
session.auth = ('username', 'password')  # Basic認証の例

# トランスポートの設定
transport = Transport(session=session)

# WSDLからクライアントを作成
client = Client('http://example.com/stockservice?wsdl', transport=transport)

# サービスメソッドの呼び出し - シンプルな例
result = client.service.GetStockPrice(StockName='GOOG')
print(f"Google stock price: {result.Price}")

# ヘッダーの設定例
header = {
    'Authentication': {
        'ApiKey': 'ABC123XYZ'
    }
}
client.transport.session.headers.update({'SOAPAction': 'http://example.com/GetStockPrice'})

# 複雑なパラメータを持つメソッド呼び出し
history_request = {
    'StockName': 'GOOG',
    'StartDate': '2025-01-01',
    'EndDate': '2025-03-01'
}
history_result = client.service.GetStockHistory(**history_request)

# 結果の処理
for price_data in history_result.History.StockPrice:
    print(f"Date: {price_data.Date}, Close: {price_data.Close}")
```

### サーバー側の実装 - Spyneライブラリ

Pythonでは、SOAPサーバーを実装する際に「Spyne」ライブラリを使用するのが一般的です。Spyneは、XMLベースのプロトコル（SOAPなど）をサポートするPythonのフレームワークです。

```python
from spyne import Application, rpc, ServiceBase, Integer, Float, String, Unicode
from spyne.protocol.soap import Soap11
from spyne.server.wsgi import WsgiApplication
from wsgiref.simple_server import make_server

# サービスクラスの定義
class StockService(ServiceBase):
    @rpc(String, _returns=Float)
    def GetStockPrice(ctx, StockName):
        """株価を取得するサービス
        @param StockName: 株式シンボル
        @return: 現在の株価
        """
        # 実際の実装ではデータベースやサードパーティAPIから株価を取得
        if StockName == 'GOOG':
            return 2500.75
        elif StockName == 'AAPL':
            return 150.25
        else:
            raise ValueError(f"Unknown stock: {StockName}")
    
    @rpc(String, String, String, _returns=Unicode)
    def GetStockHistory(ctx, StockName, StartDate, EndDate):
        """株価の履歴データを取得するサービス
        @param StockName: 株式シンボル
        @param StartDate: 開始日 (YYYY-MM-DD)
        @param EndDate: 終了日 (YYYY-MM-DD)
        @return: 株価履歴データのXML
        """
        # サンプルの履歴データを返す
        return f"""
        <History>
            <StockPrice>
                <Date>{StartDate}</Date>
                <Open>2510.50</Open>
                <High>2525.75</High>
                <Low>2505.25</Low>
                <Close>2520.25</Close>
                <Volume>1245678</Volume>
            </StockPrice>
            <StockPrice>
                <Date>{EndDate}</Date>
                <Open>2520.25</Open>
                <High>2540.50</High>
                <Low>2515.00</Low>
                <Close>2530.75</Close>
                <Volume>1356789</Volume>
            </StockPrice>
        </History>
        """

# アプリケーションの構成
application = Application(
    [StockService],  # サービスクラスのリスト
    tns='http://example.com/stock',  # ターゲット名前空間
    in_protocol=Soap11(validator='lxml'),
    out_protocol=Soap11()
)

# WSGIアプリケーションの構成
wsgi_application = WsgiApplication(application)

# サーバーの起動
if __name__ == '__main__':
    server = make_server('127.0.0.1', 8000, wsgi_application)
    print("SOAP server running at http://127.0.0.1:8000/")
    print("WSDL available at http://127.0.0.1:8000/?wsdl")
    server.serve_forever()
```

#### Spyneを使用したSOAPサーバーの特徴

1. **簡潔なサービス定義**: `ServiceBase`を継承し、`@rpc`デコレータを使用して簡単にメソッドを公開できます
2. **自動WSDL生成**: Spyneは指定されたサービスクラスからWSDLを自動生成します
3. **型の安全性**: 引数と戻り値の型を明示的に定義できます
4. **既存のWebフレームワークとの統合**: WSGIに対応しているため、DjangoやFlaskなどと統合可能です

## 5. 実際の実装例

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/4ce3e3e8-51ba-490b-9897-e38b643c5f9f.png)

### SOAPリクエストとレスポンスの例

#### リクエスト例

```xml
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope 
    xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
    xmlns:stock="http://example.com/stock">
    
    <soap:Header>
        <stock:Authentication>
            <stock:ApiKey>ABC123XYZ</stock:ApiKey>
        </stock:Authentication>
    </soap:Header>
    
    <soap:Body>
        <stock:GetStockPrice>
            <stock:StockName>GOOG</stock:StockName>
        </stock:GetStockPrice>
    </soap:Body>
    
</soap:Envelope>
```

#### レスポンス例

```xml
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope 
    xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
    xmlns:stock="http://example.com/stock">
    
    <soap:Body>
        <stock:GetStockPriceResponse>
            <stock:Price>2500.75</stock:Price>
        </stock:GetStockPriceResponse>
    </soap:Body>
    
</soap:Envelope>
```

### エラーハンドリング（SOAPフォルト）の例

```xml
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope 
    xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
    xmlns:stock="http://example.com/stock">
    
    <soap:Body>
        <soap:Fault>
            <soap:Code>
                <soap:Value>soap:Client</soap:Value>
            </soap:Code>
            <soap:Reason>
                <soap:Text xml:lang="en">Stock symbol not found: INVALID</soap:Text>
            </soap:Reason>
            <soap:Detail>
                <stock:StockError>
                    <stock:ErrorCode>404</stock:ErrorCode>
                    <stock:ErrorMessage>The requested stock INVALID does not exist in our database</stock:ErrorMessage>
                </stock:StockError>
            </soap:Detail>
        </soap:Fault>
    </soap:Body>
    
</soap:Envelope>
```

### 複雑なデータ構造と配列の例

```xml
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope 
    xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
    xmlns:stock="http://example.com/stock">
    
    <soap:Body>
        <stock:GetStockHistoryResponse>
            <stock:StockName>GOOG</stock:StockName>
            <stock:History>
                <stock:StockPrice>
                    <stock:Date>2024-02-01</stock:Date>
                    <stock:Open>2510.50</stock:Open>
                    <stock:High>2525.75</stock:High>
                    <stock:Low>2505.25</stock:Low>
                    <stock:Close>2520.25</stock:Close>
                    <stock:Volume>1245678</stock:Volume>
                </stock:StockPrice>
                <stock:StockPrice>
                    <stock:Date>2024-02-02</stock:Date>
                    <stock:Open>2520.25</stock:Open>
                    <stock:High>2540.50</stock:High>
                    <stock:Low>2515.00</stock:Low>
                    <stock:Close>2530.75</stock:Close>
                    <stock:Volume>1356789</stock:Volume>
                </stock:StockPrice>
                <!-- 追加のデータ... -->
            </stock:History>
        </stock:GetStockHistoryResponse>
    </soap:Body>
    
</soap:Envelope>
```

## 6. SOAPとRESTの比較

### 特徴の比較

| 特徴 | SOAP | REST |
|------|------|------|
| メッセージフォーマット | XML限定 | JSON, XML, HTML, プレーンテキストなど柔軟 |
| プロトコル | HTTP, SMTP, TCPなど | 主にHTTP/HTTPS |
| サービス定義 | WSDLで形式的に定義 | 通常は非公式（OpenAPIなどで定義することも） |
| 状態 | ステートレス（基本的に） | ステートレス |
| セキュリティ | 標準化された多くの機能（WS-Security） | HTTPSに依存、または個別実装 |
| キャッシュ | 制限あり | HTTPのキャッシュ機能を利用可能 |
| 学習曲線 | 比較的高い | 比較的低い |
| 処理オーバーヘッド | 大きい（XMLパース、エンベロープ） | 小さい |
| ユースケース | エンタープライズ系、金融、決済システム | Webサービス、モバイルアプリ、マイクロサービス |

### 選定の目安

SOAPを選ぶ場合：
- 厳格な契約とインターフェース定義が必要
- 複雑なトランザクションや高度なセキュリティ要件がある
- 標準化された企業内システムとの連携
- WS-Securityなどの拡張機能が必要

RESTを選ぶ場合：
- シンプルなデータ交換が主目的
- 高いパフォーマンスと低いリソース使用量が要求される
- 公開APIの提供
- モバイルアプリケーションとの連携

## 7. セキュリティ上の注意点

SOAPサービスを実装・利用する際の主なセキュリティ上の注意点：

### 認証と認可

- **WS-Security**: XMLセキュリティのための標準規格
- **SAML（Security Assertion Markup Language）**: 認証・認可情報の交換規格
- **OAuth/OIDC**: 最新の認証・認可フレームワークとの統合

```xml
<!-- WS-Securityヘッダーの例 -->
<soap:Header>
    <wsse:Security 
        xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
        soap:mustUnderstand="1">
        
        <wsse:UsernameToken>
            <wsse:Username>user123</wsse:Username>
            <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">pass456</wsse:Password>
            <wsse:Nonce>dGhpcyBpcyBhIG5vbmNl</wsse:Nonce>
            <wsu:Created>2025-03-01T12:00:00Z</wsu:Created>
        </wsse:UsernameToken>
        
    </wsse:Security>
</soap:Header>
```

### データの保護

- **XML暗号化**: メッセージ内の機密データの暗号化
- **XMLデジタル署名**: メッセージの改ざん検出と送信元証明
- **TLS/SSL**: トランスポート層での暗号化

### 一般的な脆弱性対策

- **XMLインジェクション対策**: 入力検証と適切なエスケープ処理
- **XXE（XML External Entity）攻撃対策**: 外部エンティティの禁止
- **DoS攻撃対策**: メッセージサイズ制限、複雑なXMLの制限

## 8. まとめ

![soap-summary-improved.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/aa39a332-dd45-42fa-8d15-29e3ce1b3202.png)

SOAP APIは、特に企業システムや重要なトランザクション処理において、今なお重要な役割を果たしています。主な特徴は：

- XMLベースのメッセージング構造
- WSDLによる正式なサービス定義
- 厳格なデータ型とインターフェース
- 豊富なセキュリティ機能と拡張性

RESTがシンプルさと軽量性で人気を集める中でも、SOAPは以下のような状況で選択されます：

- 複雑なビジネスロジックと厳格な契約が必要な場合
- 標準化されたセキュリティプロトコルが要件の場合
- トランザクションの整合性が重要な場合
- レガシーシステムとの統合が必要な場合

効果的なSOAP API実装のためには、XMLとWSDLの理解、適切なセキュリティ対策の実施、そして様々な言語・フレームワークでの実装方法の習得が重要です。

## 参考リソース

- [W3C SOAP仕様](https://www.w3.org/TR/soap/)
- [W3C WSDL仕様](https://www.w3.org/TR/wsdl/)
- [OASIS WS-Security仕様](https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=wss)
- [JAX-WS (Java API for XML Web Services)](https://javaee.github.io/metro-jax-ws/)