soap-errorssoap-1-1soap-1-2wcfcontent-type

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)

SOAPless Team7 min read

このエラーは 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.1SOAP 1.2
Content-Typetext/xml; charset=utf-8application/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 transporthttp://schemas.xmlsoap.org/soap/httphttp://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/xmlapplication/soap+xml の違いを意識する必要がなくなります。

サービスが複数の SOAP バージョンを公開している場合も、SOAPless が登録時に正しい binding を固定するため、下流でバージョン不一致が起きることはありません。