Recreating a Message in WCF - c#

I am retrieving data from a server via WCF (I am the client). Unfortunately, the server (which I have no control of) is sometimes returning invalid XML.
In order to fix this, I plan to add a IClientMessageInspector (on the client), which modifies the Message before WCF has chance to parse the returned XML.
My first step was to implement IClientMessageInspector, but have it so it leaves the response unchanged (it should effectively be a no-op), but for some reason it causes the generated WCF method (client.getBar() below) to return a null object, rather than a populated object.
class UTF8Policer : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
Message revised = null;
var contents = new StringBuilder();
var writer = XmlWriter.Create(contents);
reply.WriteMessage(writer);
writer.Flush();
revised = Message.CreateMessage(reply.Version, reply.Headers.Action, XmlReader.Create(new StringReader(contents.ToString()));
revised.Headers.CopyHeadersFrom(reply);
revised.Properties.CopyProperties(reply.Properties);
reply = revised;
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
return null;
}
}
However, when running:
var client = new Foo_RPCClient();
var header = new header();
// This is what registers the inspector
client.Endpoint.EndpointBehaviors.Add(new FooEndpointBehaviour());
var response = client.getBar(ref header, new BarRequest());
... response is null. If I comment out the inspector registration, response works.
My conclusion is therefore that I'm somehow invalidating the message within AfterReceiveReply. Can anyone advise what the correct way is to re-create the message received?
Once I've got this working, I'm hoping it'll be trivial to fixup the XML within AfterReceiveReply, so that it actually does something useful.

Ref parameter wont work with WCF.
When you do the call to the service everything is pass as an input message serialized. Then the service deserialize it and do the work and finally it return a serialized response to the client that is deserialized on the client side.
The ref parameter might compile but in no way you retain the reference to a memory pointer in the client computer. You must use the response to return back the object.

Related

How to: c# XML InfoSet to JSON

So WCF takes a JSON, and for whatever reason translates that to an XML Infoset (see: https://stackoverflow.com/a/5684703/497745). It then reads back this XML Infoset internally using the JsonReaderDelegator (see: https://referencesource.microsoft.com/#System.Runtime.Serialization/System/Runtime/Serialization/Json/JsonReaderDelegator.cs,c0d6a87689227f04).
I am doing some very in-depth modification of the WCF execution flow, and I need to reverse the XML Infoset back to the original JSON.
Is there a library either in .NET or external that can take the XML Infoset generated by WCF and convert that back to its original JSON?
Found an answer that works in my scenario with some limited modifications.
This article: https://blogs.msdn.microsoft.com/carlosfigueira/2011/04/18/wcf-extensibility-message-inspectors/ explains how to log the JSON that originally came in to WCF, even though it is only exposed as an XML InfoSet. This was the key insight. Specifically, look at its MessageToString implementation.
Below is the relevant portion of my code, based on the implementation of MessageToString in the above link, running within a class internal class MessageFormatInspector : IDispatchMessageInspector, IEndpointBehavior that I have written to inject into the WCF stack:
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
...
var s = GetJsonFromMessage(request);
...
}
private static string GetJsonFromMessage(ref Message request)
{
using (MemoryStream ms = new MemoryStream())
{
using (XmlDictionaryWriter writer = JsonReaderWriterFactory.CreateJsonWriter(ms))
{
request.WriteMessage(writer);
writer.Flush();
string json = Encoding.UTF8.GetString(ms.ToArray()); //extract the JSON at this point
//now let's make our copy of the message to support the WCF pattern of rebuilding messages after consuming the inner stream (see: https://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.idispatchmessageinspector.afterreceiverequest(v=vs.110).aspx and https://blogs.msdn.microsoft.com/carlosfigueira/2011/05/02/wcf-extensibility-message-formatters/)
ms.Position = 0; //Rewind. We're about to make a copy and restore the message we just consumed.
XmlDictionaryReader reader = JsonReaderWriterFactory.CreateJsonReader(ms, XmlDictionaryReaderQuotas.Max); //since we used a JsonWriter, we read the data back, we need to use the correlary JsonReader.
Message restoredRequestCopy = Message.CreateMessage(reader, int.MaxValue, request.Version); //now after a lot of work, create the actual copy
restoredRequestCopy.Properties.CopyProperties(request.Properties); //copy over the properties
request = restoredRequestCopy;
return json;
}
}
}
Unfortunately, the above code only works within the context of a WCF message inspector.
However, it is able to take an XMLDictionary, which has the XML InfoSet, and to using WCF's own built in JsonWriter to reverse the conversion WCF did and emit the original JSON.
I hope this helps someone save some time in the future.

Error updating Wcf message before hitting service using IDispatchOperationSelector

We are having to intercept a SOAP message before it hits our WCF service to perform the following steps:
Route the message to the correct method as the client is unable to provide us with a SOAPAction value.
Update the namespaces of the xml as the client is unable to add namespace information to the message.
The routing is not an issue, but we are having a problem with creating the message; once we recreate the message the body merely consists of "... Stream ...".
Before creating the message, the messageContent variable contains valid, correct xml.
private Message UpdateNamespaces(Message message, string methodName)
{
var memoryStream = new MemoryStream();
var xmlWriter = XmlWriter.Create(memoryStream);
message.WriteMessage(xmlWriter);
xmlWriter.Flush();
var messageContent = Encoding.UTF8.GetString(memoryStream.ToArray());
xmlWriter.Close();
// Update messageContent with corrected XML
memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(messageContent));
var xmlDictionaryReader = XmlDictionaryReader.CreateTextReader(memoryStream, new XmlDictionaryReaderQuotas());
var newMessage = Message.CreateMessage(xmlDictionaryReader, int.MaxValue, message.Version);
newMessage.Properties.CopyProperties(message.Properties);
return newMessage;
}
The messageContent is correct at the point at which we create the memoryStream, but as soon as I check the content of newMessage.ToString(), I'm getting the "... Stream ..." body content.
If anyone could help, I'd be very grateful as I'm out of ideas!
Many thanks
I think you are mixing behavior responsibilities.
IDispatchOperationSelector.SelectOperation is intended for routing decisions. It gives you a reference to the message, but the intention is to enable access to message properties. WCF does not expect the you to modify the message using this extension point. I can't say definitively, but the problem could be that simple.
If you want to alter the message namespace you should be using IDispatchMessageInspector. I suggest creating a second behavior for that task. Here's a nice Message Inspector example.

Retrieving object sent through a Message Queue

I am building a Queue system and I have been able to send an object to a public queue located on another server.
What I cannot figure out is how to rebuild the object on the receiver side (I have the its definition on both ends).
Any ideas?
Have a look at the following MSDN example: http://msdn.microsoft.com/en-us/library/y918yfy2(v=vs.110).aspx
Basically, calling queue.Send(object) serializes the object with the default XmlMessageFormatter.
So you'll have to deserialize the message using the same serializer, and cast the result of the received Message.Body to the good type:
// Connect to the a queue on the local computer.
MessageQueue myQueue = new MessageQueue(".\\myQueue");
// Set the formatter to indicate body contains an Order.
myQueue.Formatter = new XmlMessageFormatter(new Type[] {typeof(MyProject.Order)});
// Receive and format the message.
Message myMessage = myQueue.Receive();
Order myOrder = (Order)myMessage.Body;

Get HTTP Header in WCF MessageEncoder

I'm currently writing a GZIP compression for my selfhosted WCF REST application. I have a custom implementation of the .NET 'MessageEncoder' class and a custom implementation of the 'WebContentTypeMapper' class .
How can I retrieve the http headers in the 'ReadMessage' function and in the 'GetMessageFormatForContentType' function? I'd like to check the incoming request for the 'Content-Encoding' header before decompressing the input.
Thank You.
This is what you can do
if (WebOperationContext.Current.IncomingRequest.Headers["Content-Encoding"] == WHAT YOU WANT)
{
// Do what you like to do here
}
Hope this helps.
Thanks.
You could try with WebOperationContext.Current or OperationContext.Current (depending of your binding).
But unfortunately i think you cannot do this within the MessageEncoder implementation itself because it's too late in the process because by the time the MessageEncoder is asked to write the message contents the message frame, in this case the HTTP headers, has already been written. So, you would also need additional behavior, in the form of an IOperationBehavior, applied to your operations that sets the headers accordingly.
In one of my personnal implementation, i have solved this by adding a GzipExtension in OperationContext with a custom message inspector.
As Alex said, IIS already have a feature called dynamic compression that can compression any configured content type.
I don't believe you will be able to get directly to the header from the CustomMessageEncoder. What you may be able to do is leverage the updated .NET 4.5 WCF BinaryMessageEncoderBindingElement. This now allows you to specify the compression type (such as Gzip) and automatically detects if the message body is compressed before attempting to decompress. See Whats's New in Windows Communication Foundation 4.5 for more details.
If you want to get to the header, one way you could try is to leverage HttpRequestMessageProperty in an implementation of IDispatchMessageInspector.
Simple example:
public class MyDispatchMessageInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
object obj;
if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out obj))
{
var httpRequestMessageProperty = obj as HttpRequestMessageProperty;
if (httpRequestMessageProperty != null
&& !string.IsNullOrEmpty(httpRequestMessageProperty.Headers["content-encoding"]))
{
...
}
}
return null;
}
...
}
Another option is to access the OperationContext using the following:
int index = System.ServiceModel.OperationContext.Current.IncomingMessageHeaders.FindHeader("content-encoding", "");
string contentEncodeHeaderValue = System.ServiceModel.OperationContext.Current.IncomingMessageHeaders.GetHeader<string>(index);

WCF Request body is sometimes a stream and sometimes buffered?

I have a WCF webservice. I am trying to do some logging whenever a request is received by implementing a MessageInspector and logging in the AfterReceiveRequest() event.
For some reason whenever I send a request to the webservice using the WCFTestClient.exe everything works fine. The message is logged and the request proceeds as normal.
But when I send a request to the webservice using SOAPUI as the client, making a copy of the request message causes the body to simply show <body>... stream ...</body> and it fails to be loaded as an XML document later for the sake of validation.
I'm guessing that a request from the WCFTestClient.exe is received with a buffered message body and a request from SOAPUI is received as a streamed body? How is this possible?
Is there someway I can write some code that will safely make a copy of either version? I have yet to figure out how to safely copy a streamed version as CreateBufferedCopy() obviously does not achieve this.
Or can I configure WCF to always create a buffered message body and never a stream?
Here is the code I am using to log and copy the request message:
object IDispatchMessageInspector.AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
try
{
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();
Message copy = buffer.CreateMessage();
LogRequest(copy);
ValidateMessage(ref request);
}
catch (Exception e)
{
throw new FaultException<>()...
}
return null;
}
The copy of the request message fails to be loaded into an XML document within the ValidateMessage() method if it came from SOAPUI with a streamed body. It succeeds to be loaded as an XML document if it comes from WCFTestClient.exe with a buffered body.
void validateMessage(ref System.ServiceModel.Channels.Message message)
{
XmlDocument bodyDoc = new XmlDocument();
//This load throws exception if request came from SOAPUI with streamed body...
bodyDoc.Load(message.GetReaderAtBodyContents());
...
}
The exception thrown by the Load() method is:
System.InvalidOperationException {"The specified node cannot be
inserted as the valid child of this node, because the specified node
is the wrong type."}
at System.Xml.XmlDocument.AppendChildForLoad(XmlNode newChild,
XmlDocument doc) at
System.Xml.XmlLoader.LoadDocSequence(XmlDocument parentDoc) at
System.Xml.XmlLoader.Load(XmlDocument doc, XmlReader reader, Boolean
preserveWhitespace) at System.Xml.XmlDocument.Load(XmlReader
reader) at ...
I believe that SOAPUI always sends the message requests it builds as Stream. I do not know for sure if this is something you can modify, either by code on your SOAPUI test or some SOAPUI configuration option/file on SOAPUI.
Ckeck the TransferMode property of your binding as explained here and here. You could possibly have multiple endpoints using different custom bindings for clients that send you buffered request and stremed requests.
Hope this helps.
What's the exception being thrown? The reader returned by GetReaderAtBodyContents() is positioned at the first element inside the body, not on the body tag itself. So the way you're loading your message is incorrect because the body could contain more than one node and in that case it will fail.
Just to check, could you use the following code to validate the content of the whole message (the copy) and see if the body contains exactly the same thing when sent from SOAPUI?
using (MemoryStream stream = new MemoryStream())
{
using (XmlWriter writer = XmlWriter.Create(stream))
{
message.WriteMessage(writer);
writer.Flush();
stream.Position = 0;
}
}
If you want all nodes inside the body you might have to create a Body node yourself.
The GetReaderAtBodyContents() method returns any characters between the closing element of the body and the closing element of the soap envelope. The XmlReader fails with the exception listed earlier in the thread when it reads past the closing element of the body.
More here:
http://www.katlaconsulting.co.uk/blog/wcfxmlschemavalidation

Categories