このエラーは XML の破損ではなく、クライアントとサーバーの間で SOAP バージョンが食い違っていることを示しています。
The content type application/soap+xml; charset=utf-8 of the response message does not match the content type of the binding (text/xml; charset=utf-8)
片方が SOAP 1.2 で話し、もう片方が SOAP 1.1 で受け取ろうとしている状態です。SOAP クライアントは HTTP の Content-Type でこの不一致を検出し、XML body を解析する前にリクエストを拒否します。
SOAP 1.1 と SOAP 1.2 の違い
この 2 つのバージョンは、HTTP レベルでもエンベロープレベルでも異なります。
| 項目 | SOAP 1.1 | SOAP 1.2 |
|---|---|---|
| Content-Type | text/xml; charset=utf-8 | application/soap+xml; charset=utf-8 |
| Action の指定方法 | SOAPAction HTTP ヘッダー | Content-Type 内の action パラメータ |
| Envelope 名前空間 | http://schemas.xmlsoap.org/soap/envelope/ | http://www.w3.org/2003/05/soap-envelope |
| Fault 構造 | <faultcode> + <faultstring> | <Code> + <Reason>(サブコード付き) |
| Binding transport | http://schemas.xmlsoap.org/soap/http | http://www.w3.org/2003/05/soap/bindings/HTTP/ |
クライアントが text/xml(SOAP 1.1)で送ったのに、サーバーが application/soap+xml(SOAP 1.2)で返すと、クライアントライブラリは XML body を見る前にエラーを投げます。
ステップ 1: WSDL の binding を確認する
WSDL の <binding> 要素と transport 属性で、サービスが期待する SOAP バージョンが分かります。
SOAP 1.1 の binding 例:
<wsdl:binding name="UserServiceSoap" type="tns:UserServiceSoap">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="GetUser">
<soap:operation soapAction="http://tempuri.org/GetUser" />
</wsdl:operation>
</wsdl:binding>
SOAP 1.2 の binding 例:
<wsdl:binding name="UserServiceSoap12" type="tns:UserServiceSoap">
<soap12:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="GetUser">
<soap12:operation soapAction="http://tempuri.org/GetUser" />
</wsdl:operation>
</wsdl:binding>
名前空間プレフィックスが soap: なら SOAP 1.1、soap12: なら SOAP 1.2 です。多くのサービスが両方の binding を別々のポートやエンドポイントで公開しています。
ステップ 2: curl で実際のレスポンスを確認する
クライアントコードを修正する前に、サーバーが何を返しているかを確認します。
# サーバーが返す Content-Type を確認
curl -s -D - -o /dev/null \
-H "Content-Type: text/xml; charset=utf-8" \
-H 'SOAPAction: "http://tempuri.org/GetUser"' \
-d '<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetUser xmlns="http://tempuri.org/">
<userId>1</userId>
</GetUser>
</soap:Body>
</soap:Envelope>' \
https://example.com/service.svc
レスポンスヘッダーの Content-Type を確認してください。text/xml で送ったのにレスポンスが application/soap+xml なら、サーバーは SOAP 1.2 で応答しています。クライアントを SOAP 1.2 に合わせるか、SOAP 1.1 用のエンドポイントを探す必要があります。
# SOAP 1.2 の Content-Type で試す
curl -s -D - -o /dev/null \
-H "Content-Type: application/soap+xml; charset=utf-8; action=\"http://tempuri.org/GetUser\"" \
-d '<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<GetUser xmlns="http://tempuri.org/">
<userId>1</userId>
</GetUser>
</soap12:Body>
</soap12:Envelope>' \
https://example.com/service.svc
ステップ 3: クライアント設定を修正する
WCF (.NET) — web.config / app.config
このエラーの最も多い原因は、WCF クライアントが basicHttpBinding(SOAP 1.1)を使っているのに、サーバーが SOAP 1.2 を期待しているケースです。
<!-- 誤り: basicHttpBinding は SOAP 1.1 (text/xml) を使用 -->
<bindings>
<basicHttpBinding>
<binding name="UserServiceSoap" />
</basicHttpBinding>
</bindings>
<!-- 正しい: customBinding で SOAP 1.2 の textMessageEncoding を指定 -->
<bindings>
<customBinding>
<binding name="UserServiceSoap12">
<textMessageEncoding messageVersion="Soap12" />
<httpTransport />
</binding>
</customBinding>
</bindings>
サーバーが SOAP 1.1 に対応している場合は、endpoint の address が SOAP 1.1 用のポートを指しているか確認してください。
<client>
<endpoint address="https://example.com/service.svc/soap11"
binding="basicHttpBinding"
bindingConfiguration="UserServiceSoap"
contract="UserService.IUserService" />
</client>
Java (JAX-WS)
Java では SOAP バージョンはサービス側の @BindingType アノテーション、またはクライアント生成時の binding ID で制御します。
import javax.xml.ws.BindingProvider;
import javax.xml.ws.soap.SOAPBinding;
// SOAP 1.2 のポートを取得
UserService service = new UserService();
UserServiceSoap port = service.getUserServiceSoap12();
BindingProvider bp = (BindingProvider) port;
// 必要に応じてエンドポイントを指定
bp.getRequestContext().put(
BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
"https://example.com/service"
);
wsimport やビルドプラグインで client stub を再生成する際、正しい binding を選択しているか確認してください。
.NET (HttpClient を直接使う場合)
WCF を使わず手動でリクエストを組み立てる場合は、Content-Type の指定に注意が必要です。
// SOAP 1.2 の場合
var content = new StringContent(soapEnvelope, Encoding.UTF8, "application/soap+xml");
// SOAP 1.1 の場合
var content = new StringContent(soapEnvelope, Encoding.UTF8, "text/xml");
request.Headers.Add("SOAPAction", "\"http://tempuri.org/GetUser\"");
なぜこの問題が繰り返し発生するのか
「SoapUI では通るのにアプリからだと失敗する」という典型的なパターンは、ツールごとに異なる binding を選んでいることが原因です。SoapUI が SOAP 1.2 ポートを自動選択し、生成済みのクライアントが SOAP 1.1 ポートをデフォルトで使うケースは非常に多いです。
WSDL が両バージョンを公開している場合、各 consumer がそれぞれ「どちらが正しいか」を独自に発見しなければなりません。正解を一元管理する仕組みがないと、同じ問題が何度も起きます。
SOAPless でこの問題をなくす方法
SOAPless に WSDL を登録すると、エンジンが WSDL を解析し、サービスが SOAP 1.1 と 1.2 のどちらを使っているかを自動検出します。以降のすべてのリクエストで正しい Content-Type、Envelope 名前空間、Action ヘッダー形式が自動的に適用されます。下流の consumer は REST JSON エンドポイントを呼ぶだけで、text/xml と application/soap+xml の違いを意識する必要がなくなります。
サービスが複数の SOAP バージョンを公開している場合も、SOAPless が登録時に正しい binding を固定するため、下流でバージョン不一致が起きることはありません。