Validating WCF Web Service XML body using a MessageInspector
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.
精彩评论