开发者

Validating WCF Web Service XML body using a MessageInspector

开发者 https://www.devze.com 2022-12-18 13:12 出处:网络
I have a built a WCF web service against a pre-existing XSD schema which uses the XmlSerializer serializer.

I have a built a WCF web service against a pre-existing XSD schema which uses the XmlSerializer serializer.

I would like to validate incoming requests and outgoing requests against this pre-existing schema. MSDN contains a worked example of how this can be accomplished using WCF MessageInspectors. The technique described involves creating an XmlReader at the body contents:

XmlReader bod开发者_如何学编程yReader = message.GetReaderAtBodyContents().ReadSubtree();

And then using validating against the SchemaSet using an XMLDictionaryReader create from this reader.

I have encountered a problem whereby my xml body contents contains several instances of xsi:type="xsd:string" against elements. The namespace prefixes for xsi and xsd are generated by WCF against the body element and so my validation fails due to xsd not being declared.

Example XML message:

<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>

Validation error:

"The value 'xsd:string' is invalid according to its schema type 'QName' - 'xsd' is an undeclared namespace."

Is there any WCF configuration options that allow me to push down these xmlns declarations into the body?


Okay, I know this question was asked long ago but I just came across the same issue so I thought I'd post my findings here.

Since the xsi and xsd namespaces are on the Body element, the message.GetReaderAtBodyContents() method will not return valid xml. I found two ways to deal with this.

First, you can wrap the call in your own element containing the xsi and xsd namespaces then you can extract the inner xml from that. This causes the namespaces to be qualified when used.

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;

If you inspect the innerBody you'll see that the xsi and xsd namespaces have been qualified on each node that uses them so you can load the innerBody into a reader for validation.

Second, you can just read the entire message into xml and extract the body contents as above. This will have the same effect as above but will handle any namespaces on the Body element.

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;

Here I just loaded the entire message into a string builder then loaded that into an XmlDocument so I could extract the inner xml of the Body element. The resulting xml will be qualified the same as in the first approach.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号