【问题标题】:Validating WCF Web Service XML body using a MessageInspector使用 MessageInspector 验证 WCF Web 服务 XML 正文
【发布时间】:2011-01-11 19:52:33
【问题描述】:

我针对使用 XmlSerializer 序列化程序的预先存在的 XSD 架构构建了 WCF Web 服务。

我想根据这个预先存在的架构验证传入请求和传出请求。 MSDN 包含一个工作示例,说明如何使用 WCF MessageInspectors 完成此操作。所描述的技术涉及在正文内容处创建一个XmlReader

XmlReader bodyReader = message.GetReaderAtBodyContents().ReadSubtree();

然后使用来自该阅读器的XMLDictionaryReader 创建对SchemaSet 进行验证。

我遇到了一个问题,即我的 xml 正文内容包含多个针对元素的 xsi:type="xsd:string" 实例。 xsixsd 的命名空间前缀是由 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 声明下推到正文中?

【问题讨论】:

  • ??? 标签上定义的 XML 命名空间 ARE .....
  • xml 命名空间是在 标签上定义的,但是被验证的 xml 是 body 标签下的子树
  • 我不知道有什么方法可以将这些命名空间向下“移动” - 你不能更改验证以包含 标记吗?可能是最简单的方法......

标签: .net wcf web-services validation xsd


【解决方案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 将与第一种方法相同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多