Constructing an Empty WCF Reply Message
We're continuing the series of posts arising from the implementation intricacies of a WCF router. Today's post features a seemingly simple task: Constructing an empty WCF reply message, which is the equivalent of the message sent in response to an operation which returns void (but is not one-way).
The motivation for this could be the following: A router needs to dispatch a message to multiple subscribers. However, the router itself is unaware of the data contract - it is willing to work on an untyped Message-based contract. The operation doesn't have a return type - it's void. On the other hand, the operation is not one-way for some reason - for example, it might require transactional semantics to assure that the message is delivered and made durable as part of the client transaction. This would require the router to construct the semantic equivalent of the message that would be sent in response to the operation if the service was working with the original contract, and not the untyped Message-based contract the router is familiar with.
Let's observe the request and reply messages for a simple operation called Hello which takes a string parameter. The request message looks like this:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">
http://tempuri.org/IHello/Hello
</a:Action>
<a:MessageID>urn:uuid:2ec62f21-b334-4e50-85e0-586decdef121</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">http://localhost:9090/Hello</a:To>
</s:Header>
<s:Body>
<Hello xmlns="http://tempuri.org/">
<str>Hello World!</str>
</Hello>
</s:Body>
</s:Envelope>
And the response message looks like this:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">
http://tempuri.org/IHello/HelloResponse
</a:Action>
<a:RelatesTo>
urn:uuid:2ec62f21-b334-4e50-85e0-586decdef121
</a:RelatesTo>
</s:Header>
<s:Body>
<HelloResponse xmlns="http://tempuri.org/" />
</s:Body>
</s:Envelope>
This seems simple enough to fake. The RelatesTo header is not critical (it provides the correlation between the request and response messages, but we can live without it). The Action header must be present, and the response body has to be present because that's what the operation formatter on the client side expects.
I wasn't able to find an elegant way to generate the response - all relevant classes which implement IDispatchOperationFormatter are internal, and considered an implementation detail. Therefore, I had to resort to the following (highly fragile) code to construct the response message:
XmlDictionaryReader requestReader =
message.GetReaderAtBodyContents();
string bodyElemName = requestReader.Name;
string bodyNS = requestReader.NamespaceURI;
requestReader.Close();
XmlDictionaryReader bodyReader =
XmlDictionaryReader.CreateDictionaryReader(
XmlReader.Create(
new StringReader(
"<" + bodyElemName + "Response " +
"xmlns='" + bodyNS + "' />"
)));
Message reply = Message.CreateMessage(
message.Version,
message.Headers.Action + "Response",
bodyReader);
It works, but if there's a better way that you can think of, I'd be delighted to hear about it.