我针对使用XmlSerializer
序列化程序的预先存在的XSD架构构建了一个WCF Web服务。
我想针对此预先存在的架构验证传入请求和传出请求。 MSDN包含一个如何使用WCF MessageInspectors完成此操作的实例。所描述的技术涉及在主体内容上创建XmlReader
:
XmlReader bodyReader = message.GetReaderAtBodyContents().ReadSubtree();
然后使用此读者使用SchemaSet
创建对XMLDictionaryReader
进行验证。
我遇到了一个问题,即我的xml正文内容包含多个xsi:type="xsd:string"
针对元素的实例。 xsi
和xsd
的名称空间前缀由WCF针对body
元素生成,因此我的验证因xsd
未被声明而失败。
示例XML消息:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://www.abc.com/Service/Response</Action>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<foo xmlns="http://www.abc.com">
<aaa xsi:type="xsd:string">true</aaa>
</foo>
</s:Body>
</s:Envelope>
验证错误:
"The value 'xsd:string' is invalid according to its schema type 'QName' - 'xsd' is an undeclared namespace."
是否有任何WCF配置选项允许我将这些xmlns
声明下推到正文中?
答案 0 :(得分:1)
好的,我知道很久以前就问过这个问题,但我刚遇到同样的问题,所以我想我会在这里发布我的发现。
由于xsi和xsd命名空间位于Body元素上,因此message.GetReaderAtBodyContents()方法不会返回有效的xml。我找到了两种方法来解决这个问题。
首先,您可以将调用包装在包含xsi和xsd命名空间的自己的元素中,然后可以从中提取内部xml。这会导致命名空间在使用时被限定。
XmlReader bodyReader = message.GetReaderAtBodyContents();
// Next we wrap the possibly invalid body contents (because of missing namespaces) into our own wrapper with the namespaces specified
XmlDocument bodyDoc = new XmlDocument();
MemoryStream bodyMS = new MemoryStream();
XmlWriter w = XmlWriter.Create(bodyMS, new XmlWriterSettings {Indent = true, IndentChars = " ", OmitXmlDeclaration = true});
w.WriteStartElement("body");
w.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance");
w.WriteAttributeString("xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema");
while (xdr.NodeType != XmlNodeType.EndElement && xdr.LocalName != "Body" && xdr.NamespaceURI != "http://schemas.xmlsoap.org/soap/envelope/")
{
if (xdr.NodeType != XmlNodeType.Whitespace)
{
w.WriteNode(xdr, true);
}
else
{
xdr.Read();
}
}
w.WriteEndElement();
w.Flush();
bodyMS.Position = 0;
bodyDoc.Load(bodyMS);
XmlNode bodyNode = bodyDoc.SelectSingleNode("body");
string innerBody = bodyNode.InnerXml;
如果检查innerBody,您将看到xsi和xsd命名空间已经在使用它们的每个节点上进行了限定,因此您可以将innerBody加载到读取器中进行验证。
其次,您可以将整个消息读入xml并提取正文内容,如上所述。这将具有与上面相同的效果,但将处理Body元素上的任何名称空间。
StringBuilder sb = new StringBuilder();
using (System.Xml.XmlWriter xw = System.Xml.XmlWriter.Create(sb))
{
message.WriteMessage(xw);
}
string theWholeMessage = sb.ToString();
XmlDocument wholeBodyDoc = new XmlDocument();
wholeBodyDoc.LoadXml(theWholeMessage);
XmlNamespaceManager wholeNS = new XmlNamespaceManager(new NameTable());
wholeNS.AddNamespace("s", "http://www.w3.org/2003/05/soap-envelope");
XmlNode wholeBodyNode = wholeBodyDoc.SelectSingleNode("//s:Body", wholeNS);
string innerBody = wholeBodyNode.InnerXml;
这里我只是将整个消息加载到字符串构建器中,然后将其加载到XmlDocument中,这样我就可以提取Body元素的内部xml。生成的xml将与第一种方法相同。