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

来源:互联网 发布:数据管道是什么 编辑:程序博客网 时间:2024/05/16 12:54

Refereced from: 

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 usedsoapUI 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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14

------=_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

 

1
2
3
4
5
6
7
8
9

<?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.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

/* 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.

 

1

<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.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140

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

1
2
3
4
5
6
7
8
9
10
11
12

<?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>

原创粉丝点击