我使用“添加服务引用”功能为外部Web服务创建代理。
默认情况下,WCF客户端正在生成SOAP消息,其中消息体具有如下所示的命名空间装饰:
<s:Body>
<BankingTransaction xmlns="http://tempuri.org/">
<amount>0</amount>
</BankingTransaction>
</s:Body>
我需要消息体看起来像这样
<s:Body>
<bb:BankingTransaction xmlns:bb="http://tempuri.org/">
<amount>0</amount>
</bb:BankingTransaction>
</s:Body>
区别在于“bb”xml名称空间别名。我正在尝试使用的Web服务要求将消息有效内容的xml命名空间设置为别名。 WCF客户端的默认行为是将名称空间定义为DEFAULT名称空间。我已经搜索了这个问题的配置/装饰解决方案的高低,并没有找到它。除了配置解决方案之外,我必须在序列化后检查和更改每个SOAP消息。 #lame。
这里有一个简单的解决方案吗?
答案 0 :(得分:3)
此问题的解决方案是创建自定义MessageInspector (通过 IClientMessageInspector )以在发送之前检查和更改WCF客户端代理生成的SOAP消息通过电线。这个解决方案的基础在Steven Cheng的帖子"[WCF] How to modify WCF message via custom MessageInspector"中有所阐述,还有Kirk Evan的帖子"Modify Message Content with WCF"的进一步背景。
我使用了Steven的帖子中的代码来连接自定义的MessageInspector基础结构。然后我修改了他的Transformf2()方法,该方法仅在SOAP消息的<Body>
部分运行,以满足我的特定需求。就我而言,正如原始问题中所述,我需要为上面的目标Web服务XML命名空间xmlns="http://tempuri.org"
定义和使用别名。
要做到这一点,我必须
<BankingTransaction>
的引用,
这将永远是<Body>
执行此操作的修改后的Transform2()代码如下:
private static Message Transform(Message oldMessage)
{
//load the old message into XML
var msgbuf = oldMessage.CreateBufferedCopy(int.MaxValue);
var tmpMessage = msgbuf.CreateMessage();
var xdr = tmpMessage.GetReaderAtBodyContents();
var xdoc = new XmlDocument();
xdoc.Load(xdr);
xdr.Close();
// We are making an assumption that the Operation element is the
// first child element of the Body element
var targetNodes = xdoc.SelectNodes("./*");
// There should be only one Operation element
var node = (null != targetNodes) ? targetNodes[0] : null;
if (null != node)
{
if(null != node.Attributes) node.Attributes.RemoveNamedItem("xmlns");
node.Prefix = "bb";
}
var ms = new MemoryStream();
var xw = XmlWriter.Create(ms);
xdoc.Save(xw);
xw.Flush();
xw.Close();
ms.Position = 0;
var xr = XmlReader.Create(ms);
//create new message from modified XML document
var newMessage = Message.CreateMessage(oldMessage.Version, null, xr);
newMessage.Headers.CopyHeadersFrom(oldMessage);
newMessage.Properties.CopyProperties(oldMessage.Properties);
return newMessage;
}
}