Possible Duplicate:
WCF: Data contract being converted to message contract
I have a generic WCF proxy, for those unfamiliar with that it is a class with a method with the below OperationContract.
[OperationContract(Action = "*", ReplyAction = "*")]
void Proxy(Message requestMessage);
One of the requirements that has recently emerged, is I need to replace all properties in the Message that are of type IPAddress, to the IPAddress value of those that supplied the request message.
I.e. something like,
public void Proxy(Message requestMessage)
{
try
{
// Client IP address, not currently used!
IPAddress clientIP = IPAddress.Parse((requestMessage
.Properties[RemoteEndpointMessageProperty.Name]
as RemoteEndpointMessageProperty).Address);
var factory = new ChannelFactory<IDmzProxy>("client");
IDmzProxy dmzProxy = factory.CreateChannel();
dmzProxy.Proxy(requestMessage);
factory.Close();
}
// No leakage of data! Any exceptions still return void!
catch (Exception exception)
{
Log.Fatal(
"Exception occurred on proxying the request",
exception);
return;
}
}
The question now is how to set the element in requestMessage of type IPAddress to the clientIP that I have retrieved?
Edit 1
Things I have tried and failed,
requestMessage.GetBodyAttribute("ipAddress", "http://schemas.datacontract.org/2004/07/System.Net")
Edit 2
One method seems to be replacing the XML of the MessageBody. It seems like overkill to me (what's the point of WCF then?).
It also isn't particularly easy as the MessageBody needs to match an element by its attribute names rather than the element name.
<ipAddress xmlns:a="http://schemas.datacontract.org/2004/07/System.Net" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:m_Address>3772007081</a:m_Address>
<a:m_Family>InterNetwork</a:m_Family>
<a:m_HashCode>0</a:m_HashCode>
<a:m_Numbers xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<b:unsignedShort>0</b:unsignedShort>
<b:unsignedShort>0</b:unsignedShort>
<b:unsignedShort>0</b:unsignedShort>
<b:unsignedShort>0</b:unsignedShort>
<b:unsignedShort>0</b:unsignedShort>
<b:unsignedShort>0</b:unsignedShort>
<b:unsignedShort>0</b:unsignedShort>
<b:unsignedShort>0</b:unsignedShort>
</a:m_Numbers>
<a:m_ScopeId>0</a:m_ScopeId>
</ipAddress>
Edit 3
Certainly not a duplicate, here is something that's roughly working, needs some work still to replace the node I am after,
public void Proxy(Message requestMessage)
{
try
{
Log.Info("Received request");
requestMessage = SourceNATMessage(requestMessage);
// Check if there is an accepted action we have to catch
// If the Accepted Action is set, and the action is not the same
// then just return);)
if (!String.IsNullOrEmpty(AcceptedAction) &&
!requestMessage.Headers.Action.EndsWith(AcceptedAction))
{
Log.WarnFormat(
"Invalid request received with the following action {0}\n" +
"expected action ending with {1}",
requestMessage.Headers.Action,
AcceptedAction);
return;
}
// otherwise, let's proxy the request
Log.Debug("Proceeding with forwarding the request");
var factory = new ChannelFactory<IDmzProxy>("client");
IDmzProxy dmzProxy = factory.CreateChannel();
dmzProxy.Proxy(requestMessage);
factory.Close();
}
// No leakage of data! Any exceptions still return void!
catch (Exception exception)
{
Log.Fatal(
"Exception occurred on proxying the request",
exception);
return;
}
}
private static Message SourceNATMessage(Message message)
{
IPAddress clientIp =
IPAddress.Parse(
((RemoteEndpointMessageProperty)
message.Properties[
RemoteEndpointMessageProperty.Name]).Address);
Log.DebugFormat("Retrieved client IP address {0}", clientIp);
var stringBuilder = new StringBuilder();
XDocument document;
using (XmlWriter writer = XmlWriter.Create(stringBuilder))
{
message.WriteBody(writer);
writer.Flush();
document = XDocument.Parse(stringBuilder.ToString());
}
var deserializer = new DataContractSerializer(typeof(IPAddress));
foreach (XElement element in
from element in document.DescendantNodes().OfType<XElement>()
let aNameSpace = element.GetNamespaceOfPrefix("a")
let iNameSpace = element.GetNamespaceOfPrefix("i")
where
aNameSpace != null &&
aNameSpace.NamespaceName.Equals(SystemNetNameSpace) &&
iNameSpace != null &&
iNameSpace.NamespaceName.Equals(XmlSchemaNameSpace) &&
deserializer.ReadObject(element.CreateReader(), false) is IPAddress
select element)
{
element.ReplaceWith(new XElement(element.Name, deserializer.WriteObject());
}
return Message.CreateMessage(message.Version,
message.Headers.Action,
document.CreateReader());
}
Edit 4
Working code for those interested, can't post as an answer as the question is closed.
private static Message SourceNatMessage(Message message)
{
IPAddress clientIp =
IPAddress.Parse(
((RemoteEndpointMessageProperty)
message.Properties[
RemoteEndpointMessageProperty.Name]).Address);
Log.DebugFormat("Retrieved client IP address {0}", clientIp);
var stringBuilder = new StringBuilder();
XDocument document;
using (XmlWriter writer = XmlWriter.Create(stringBuilder))
using (XmlDictionaryWriter dictionaryWriter =
XmlDictionaryWriter.CreateDictionaryWriter(writer))
{
message.WriteBodyContents(dictionaryWriter);
dictionaryWriter.Flush();
document = XDocument.Parse(stringBuilder.ToString());
}
var deserializer = new DataContractSerializer(typeof(IPAddress));
var clientIpXml = new StringBuilder();
using (var xmlWriter = XmlWriter.Create(clientIpXml))
{
deserializer.WriteObject(xmlWriter, clientIp);
xmlWriter.Flush();
}
var clientElement = XElement.Parse(clientIpXml.ToString());
foreach (XElement element in
from element in document.DescendantNodes().OfType<XElement>()
let aNameSpace = element.GetNamespaceOfPrefix("a")
let iNameSpace = element.GetNamespaceOfPrefix("i")
where
aNameSpace != null &&
aNameSpace.NamespaceName.Equals(SystemNetNameSpace) &&
iNameSpace != null &&
iNameSpace.NamespaceName.Equals(XmlSchemaNameSpace) &&
element.NodeType == XmlNodeType.Element
select element)
{
try
{
deserializer.ReadObject(element.CreateReader(), false);
element.ReplaceNodes(clientElement);
}
catch (SerializationException) { }
}
Message sourceNatMessage = Message.CreateMessage(message.Version,
null,
document.CreateReader());
sourceNatMessage.Headers.CopyHeadersFrom(message);
sourceNatMessage.Properties.CopyProperties(message.Properties);
return sourceNatMessage;
}