Reputation: 1583
I have an application that interacts with a 3rd party SOAP web service, so I set up a service reference and everything works great.
However, I need to do some bug testing where I need the web service to respond with a very specific XML payload (and since it's a 3rd party service, I can't make it do whatever I want).
I was trying to use a message inspector's "AfterReceiveReply" method to override the response with the desired XML (which was captured from a different legitimate response, so I know it's valid XML). My code looks like this (based off of https://blogs.msdn.microsoft.com/kaevans/2008/01/08/modify-message-content-with-wcf/) :
public void AfterReceiveReply(ref Message reply, object correlationState)
{
// Override payload
string overrideXML = Properties.Resources.WCFOverride.Replace("---ACCOUNT_NUMBER---", "12345");
MemoryStream ms = new MemoryStream(UTF8Encoding.UTF8.GetBytes(overrideXML));
ms.Position = 0;
XmlDictionaryReaderQuotas quotas = new XmlDictionaryReaderQuotas();
XmlDictionaryReader xdr = XmlDictionaryReader.CreateBinaryReader(ms, quotas);
Message replacedMessage = Message.CreateMessage(reply.Version, null, xdr);
replacedMessage.Headers.CopyHeadersFrom(reply.Headers);
replacedMessage.Properties.CopyProperties(reply.Properties);
reply = replacedMessage;
}
When the code runs, I get an error on the Message.CreateMessage(...) line:
System.Xml.XmlException: The input source is not correctly formatted.
In the debugger at that point, I can see the correct XML in the MemoryStream, but the XmlDictionaryReader (xdr) seems to be empty. If I try to run xdr.Read() manually, I also get the same exception. I even tried a simple XML structure like:
overrideXML = "<Document></Document>";
...and it didn't work, so I think I'm setting up the XML reader incorrectly...
Anyone know what I'm doing wrong?
EDIT: I updated the code to use a TextReader instead of BinaryReader:
XmlDictionaryReader xdr = XmlDictionaryReader.CreateTextReader(ms.ToArray(), XmlDictionaryReaderQuotas.Max);
and now I no longer get the exception, but the updated reply object doesn't look right.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<ns:ResponseHeader xmlns:ns="[redacted]" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/">
[redacted]
</ns:ResponseHeader>
</s:Header>
<s:Body>... stream ...</s:Body>
</s:Envelope>
It literally has the string value "... stream ..." inside the body instead of the actual body contents. The end result is that I get a null object in response to the WCF call, instead of the expected object.
Upvotes: 1
Views: 2126
Reputation: 1583
The "...stream..." body is a red herring. The body was actually still there, but the reply.ToString() was simply returning something that was essentially a placeholder. The full message could still be accessed via a buffered copy, as described in this post: WCF message body showing <s:Body>... stream ...</s:Body> after modification
The XmlDictionaryReader.CreateTextReader() approach seemed to be able to read the XML but it wasn't able to find the body correctly (tested using the reply.GetBody() method), so after the message got back to WCF, it couldn't deserialize the object, because the XmlDictionaryReader thought the Envelope element was the body.
Using just a XmlReader worked correctly and was simpler, too:
public void AfterReceiveReply(ref Message reply, object correlationState)
{
// Override payload
string overrideXML = Properties.Resources.WCFOverride.Replace("---ACCOUNT_NUMBER---", "12345");
// Create the reader
XmlReader envelopeReader = XmlReader.Create(new StringReader(overrideXML));
// Create the message using the reader
System.ServiceModel.Channels.Message replacedMessage = System.ServiceModel.Channels.Message.CreateMessage(envelopeReader, int.MaxValue, reply.Version);
replacedMessage.Headers.CopyHeadersFrom(reply.Headers);
replacedMessage.Properties.CopyProperties(reply.Properties);
reply = replacedMessage;
}
Upvotes: 2