Client found response content type of ‘multipart/related’, but expected ‘text/xml’.

来源:互联网 发布:mac电脑excel怎么换行 编辑:程序博客网 时间:2024/05/16 20:29

Reference: http://coding.baneworld.nl/2009/02/client-found-response-content-type-of-multipartrelated-but-expected-textxml/

 

While creating a desktop client for a third party SOAP Web service I ran into the following error.
Client found response content type of ‘multipart/related’, but expected ‘text/xml’.
After trying a few different methods to generate the SOAP interfaces and classes I still was gettings errors.
Spending some time “googling” for a solution I found some references MTOM, Java/.NET interop issues, HTTP Status: 100 Continue, MIME and several other possible cauzes.

 

Just to make sure the webservice was working correctly I used soapUI to check all operations. Everything was working fine.
Testing all SOAP operations from .NET i found out that all invalid calls returned a correct SOAP response containing the error.
All valid calls generated the Client found response content type of ‘multipart/related’, but expected ‘text/xml’ error.

Using a custom SoapExtension i dumped all requests and response to log files. All valid ops requests got a response containing the expected ops result with one big issue. The response was in MIME and not the SOAP XML .NET expected.

 

------=_Part_0_9729661.1239018752715
Content-Type: application/xop+xml; charset=UTF-8; type="text/xml";
Content-Transfer-Encoding: binary
Content-ID: <root.message@cxf.apache.org>

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header><api-version>1.5.8</api-version></soap:Header>
<soap:Body>
<ns2:getDatabaseFieldsResponse xmlns:ns2="http://api.xxxxxxxx.xxx/">
<databaseField><label>Email-adres</label><name>email</name><defaultValue></defaultValue><type>email</type><key>true</key><mandatory>true</mandatory><minimumLength>0</minimumLength><maximumLength>0</maximumLength></databaseField>
</ns2:getDatabaseFieldsResponse>
</soap:Body>
</soap:Envelope>
------=_Part_0_9729661.1239018752715--


instead of

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header><api-version>1.5.8</api-version></soap:Header>
<soap:Body>
<ns2:getDatabaseFieldsResponse xmlns:ns2="http://api.xxxxxxxx.xxx/">
<databaseField><label>Email-adres</label><name>email</name><defaultValue></defaultValue><type>email</type><key>true</key><mandatory>true</mandatory><minimumLength>0</minimumLength><maximumLength>0</maximumLength></databaseField>
</ns2:getDatabaseFieldsResponse>
</soap:Body>
</soap:Envelope>

 

After spending some extra time searching the Web for anwsers, which I didn’t find, i figured i could do a simple ‘Quick & Dirty’ solution using a SoapExtension to fix the response.
By using a custom SoapExtension I could intercept the response and modify the content so .NET would except it. Info on creating and using a SoapExtension to intercept SOAP calls can be found
here (MSDN).

First thing I needed to do was write a small regular expression to extract the SOAP Envelope from the response stream.
The regex I use is “<soap:Envelope(.*?/n*?)*?</soap:Envelope>“.
Using this regex on the response content matches the SOAP Envelope, which I then use to make a correct response stream. By making a new Stream which I fill with a xml declaration and the SOAP Envelope, replacing the original Stream with this new Stream.

 

/* RegEx to select the soap Envelope */
Regex m_Regex = new Regex("<soap:Envelope(.*?/n*?)*?</soap:Envelope>", RegexOptions.IgnoreCase | RegexOptions.Compiled);

/* fix stream */
protected internal void FixStream(Stream from, Stream to)
{
TextReader reader = new StreamReader(from);
TextWriter writer = new StreamWriter(to);
string content = reader.ReadToEnd();
string newcontent = "";

Match regMatch;

for (regMatch = m_Regex.Match(content); regMatch.Success; regMatch = regMatch.NextMatch())
{
// Process the words
int nStart = regMatch.Index;
int nLenght = regMatch.Length;
newcontent += content.Substring(nStart, nLenght);
}

writer.WriteLine("<?xml version="1.0" encoding="utf-8"?>;" + newcontent);
writer.Flush();
}
Having made my SoapExtension, which I gave the name ModifierExtension, all I needed to do was add the custom soapExtensionType to the system.web/webServices/soapExtensionTypes section of the App.config file.
<add group="Low" priority="1" type="Minc.SoapExtensions.ModifierExtension, Minc.SoapExtensions" />

Now the desktop client works as expected, getting a correct response on each call.

Complete source code for ModifierExtension.

 

using System;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.IO;

using System.Text.RegularExpressions;

namespace Minc.SoapExtensions
{
public class ModifierExtension : SoapExtension
{
private Stream m_OldStream;
private Stream m_NewStream;
private Regex m_Regex;

// Save the Stream representing the SOAP request or SOAP response into a local memory buffer.
public override Stream ChainStream(Stream stream)
{
m_OldStream = stream;
m_NewStream = new MemoryStream();
return m_NewStream;
}

public ModifierExtension()
{
/* RegEx to select the soap Envelope */
m_Regex = new Regex("<soap:Envelope(.*?/n*?)*?</soap:Envelope>", RegexOptions.IgnoreCase | RegexOptions.Compiled);
}

public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
{
return "";
}

// The SOAP extension was configured to run using a configuration file
// instead of an attribute applied to a specific Web service
// method.
public override object GetInitializer(Type webServiceType)
{
return "";
}

// Receive the file name stored by GetInitializer and store it in a
// member variable for this specific instance.
public override void Initialize(object initializer)
{
}

//  If the SoapMessageStage is such that the SoapRequest or
//  SoapResponse is still in the SOAP format to be sent or received,
//  save it out to a file.
public override void ProcessMessage(SoapMessage message)
{
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
break;
case SoapMessageStage.AfterSerialize:
WriteOutput(message);
break;
case SoapMessageStage.BeforeDeserialize:
WriteInput(message);
break;
case SoapMessageStage.AfterDeserialize:
break;
default:
throw new Exception("invalid stage");
}
}

/* process incoming message*/
private void WriteInput(SoapMessage message)
{
/* set ContentType to 'text/xml' to match the modified content*/
message.ContentType = "text/xml";

FixStream(m_OldStream, m_NewStream);

m_NewStream.Position = 0;

}

/* process outgoing message*/
public void WriteOutput(SoapMessage message)
{
m_NewStream.Position = 0;
Copy(m_NewStream, m_OldStream);
}

/* copy stream */
protected internal void Copy(Stream from, Stream to)
{
TextReader reader = new StreamReader(from);
TextWriter writer = new StreamWriter(to);
writer.WriteLine(reader.ReadToEnd());
writer.Flush();
}
/* fix stream */
protected internal void FixStream(Stream from, Stream to)
{
TextReader reader = new StreamReader(from);
TextWriter writer = new StreamWriter(to);
string content = reader.ReadToEnd();
string newcontent = "";

Match regMatch;

for (regMatch = m_Regex.Match(content); regMatch.Success; regMatch = regMatch.NextMatch())
{
// Process the matches.  Should only be one Match, so newcontent += regMatch.ToSting(); would be enough.
int nStart = regMatch.Index;
int nLenght = regMatch.Length;
newcontent += content.Substring(nStart, nLenght);
}
//Add xml start tag and soap envelope to output stream
writer.WriteLine("<?xml version="1.0" encoding="utf-8"?>" + newcontent);
writer.Flush();
}
}

// Create a SoapExtensionAttribute for the SOAP Extension that can be
// applied to a Web service method.
[AttributeUsage(AttributeTargets.Method)]
public class ModifierExtensionAttribute : SoapExtensionAttribute
{

private int m_Priority;

public override Type ExtensionType
{
get { return typeof(ModifierExtension); }
}

public override int Priority
{
get { return m_Priority; }
set { m_Priority = value; }
}
}
}
Complete system.web section of App.config file
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  ...
  <system.web>
    <webServices>
      <soapExtensionTypes>
        <add group="Low" priority="1" type="Minc.SoapExtensions.ModifierExtension, Minc.SoapExtensions" />
      </soapExtensionTypes>
    </webServices>
  </system.web>
  ...
</configuration>
原创粉丝点击