{"id":10299,"date":"2005-11-01T12:20:09","date_gmt":"2005-11-01T17:20:09","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/buckh\/?p=10299"},"modified":"2019-05-06T12:20:51","modified_gmt":"2019-05-06T16:20:51","slug":"validating-xml-characters-in-soap-messages","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/buckh\/validating-xml-characters-in-soap-messages\/","title":{"rendered":"Validating XML characters in SOAP messages"},"content":{"rendered":"<p><P>I&#8217;ve written&nbsp;about using the SoapHttpClientProtocol subclasses generated by wsdl.exe several times&nbsp;over the last year,&nbsp;including handling <a href=\"http:\/\/blogs.msdn.com\/buckh\/archive\/2004\/07\/29\/201307.aspx\">authentication<\/A>, <a href=\"http:\/\/blogs.msdn.com\/buckh\/archive\/2004\/08\/13\/214364.aspx\">HTTP&nbsp;response codes<\/A>, and <a href=\"http:\/\/blogs.msdn.com\/buckh\/archive\/2005\/02\/01\/365127.aspx\">setting timeouts properly<\/A>.&nbsp; Today I needed to change the code in TFS&nbsp;to better handle characters that are not allowed in XML.<\/P>\n<P>The problem is that if you have a method on your web service that takes a String parameter, someone may call that method with a string that contains characters that are not allowed in XML.&nbsp; That input may come from a command line switch or a text box in a GUI.<\/P>\n<P>The XmlWriter used by SoapHttpClientProtocol is <A href=\"http:\/\/msdn.microsoft.com\/library\/default.asp?url=\/library\/en-us\/cpref\/html\/frlrfsystemxmlxmltextwriterclasstopic.asp\">XmlTextWriter<\/A>.&nbsp; XmlTextWriter doesn&#8217;t do any character validation.&nbsp; If the string passed to WriteString() includes characters that are not valid for XML, your XML output will also.&nbsp; The characters below 32, except for tab, carriage return, and new line, the UTF-8 BOM, and invalid surrogate pairs are not allowed by the XML standard.<\/P>\n<P>The XmlReader used by the ASP.NET web services does do character validation.&nbsp; If it finds an invalid XML character, the web service will respond with HTTP 400 Bad Request.&nbsp; That doesn&#8217;t help the user figure out what&#8217;s going on.<\/P>\n<P><A href=\"http:\/\/forums.microsoft.com\/msdn\/Search\/SearchResults.aspx?q=&amp;f=&amp;u=MTE0NTc=\">Elena Kharitidi<\/A> suggested overriding the <a href=\"http:\/\/blogs.msdn.com\/admin\/blogs\/us\/library\/system.web.services.protocols.soaphttpclientprotocol.getwriterformessage\">GetWriterForMessage()<\/A> method from SoapHttpClientProtocol in the subclass that was generated by wsdl.exe and providing a character-validating XmlWriter.<\/P>\n<P>The documentation shows an <A href=\"http:\/\/msdn.microsoft.com\/library\/default.asp?url=\/library\/en-us\/cpguide\/html\/cpconCustomizedXMLWriterCreation.asp\">example of creating a subclass of XmlTextWriter to check the characters<\/A>.&nbsp; However, it would be better to be able to use a framework class to do it without rolling our own.&nbsp; Fortunately, there is such a class in the framework.<\/P>\n<P>With a little poking around, I found the XmlCharCheckingWriter class that is internal to the framework.&nbsp; Now we just need to get the framework to give us an instance of that class.&nbsp; A little more poking around and experimentation resulted in the piece of code shown below.<\/P>\n<P>If you run it under the debugger and put a breakpoint on line 5, you&#8217;ll see that the base SoapHttpClientProtocol method returns an XmlTextWriter.&nbsp; If you step down to the XmlWriter.Create() call, you&#8217;ll see that the framework gives us the XmlCharCheckingWriter instance that we want in response to the CheckCharacters setting being true.<\/P>\n<P>Now, if you add the following code&nbsp;to your wsdl.exe-generated subclass of SoapHttpClientProtocol, you&#8217;ll get an ArgumentException on the client when trying to write the invalid XML in the SOAP message.&nbsp; The exception message will state that there is an invalid character.&nbsp; The result is a significant improvement over getting a generic HTTP 400 Bad Request from the web service.<\/P><PRE><SPAN style=\"COLOR: green\">\/\/ Override this method in order to put in character validation.<\/SPAN>\n<SPAN style=\"COLOR: blue\">protected<\/SPAN> <SPAN style=\"COLOR: blue\">override<\/SPAN> XmlWriter GetWriterForMessage(SoapClientMessage message,\n                                                 <SPAN style=\"COLOR: blue\">int<\/SPAN> bufferSize)\n{\n    XmlWriter writer = <SPAN style=\"COLOR: blue\">base<\/SPAN>.GetWriterForMessage(message, bufferSize);<\/p>\n<p>    <SPAN style=\"COLOR: green\">\/\/ Choose the encoding the same way the framework code does.<\/SPAN>\n    Encoding encoding = RequestEncoding != <SPAN style=\"COLOR: blue\">null<\/SPAN> ? RequestEncoding :\n                                                  <SPAN style=\"COLOR: blue\">new<\/SPAN> UTF8Encoding(<SPAN style=\"COLOR: maroon\">false<\/SPAN>);<\/p>\n<p>    <SPAN style=\"COLOR: green\">\/\/ We want the character validation to be done on the client side<\/SPAN>\n    <SPAN style=\"COLOR: green\">\/\/ rather than getting an obscure HTTP 400 Bad Request message<\/SPAN>\n    <SPAN style=\"COLOR: green\">\/\/ from the server (the XmlReader used by the web services does <\/SPAN>\n    <SPAN style=\"COLOR: green\">\/\/ character validation, while the writer used in the base class<\/SPAN>\n    <SPAN style=\"COLOR: green\">\/\/ does not).<\/SPAN>\n    <SPAN style=\"COLOR: green\">\/\/ We create this second XmlWriter to get an XmlCharCheckingWriter<\/SPAN>\n    <SPAN style=\"COLOR: green\">\/\/ instance.  The Create(XmlWriter, XmlWriterSettings) code path <\/SPAN>\n    <SPAN style=\"COLOR: green\">\/\/ does that (we don&#8217;t need the overhead in an XmlWellformedWriter).<\/SPAN>\n    XmlWriterSettings xws = <SPAN style=\"COLOR: blue\">new<\/SPAN> XmlWriterSettings();\n    xws.Encoding = encoding;\n    xws.Indent = <SPAN style=\"COLOR: maroon\">false<\/SPAN>;\n    xws.NewLineHandling = NewLineHandling.None;\n    xws.CheckCharacters = <SPAN style=\"COLOR: maroon\">true<\/SPAN>;      <SPAN style=\"COLOR: green\">\/\/ make sure char is valid for XML<\/SPAN>\n    writer = XmlWriter.Create(writer, xws);<\/p>\n<p>    <SPAN style=\"COLOR: blue\">return<\/SPAN> writer;\n}\n<\/PRE><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve written&nbsp;about using the SoapHttpClientProtocol subclasses generated by wsdl.exe several times&nbsp;over the last year,&nbsp;including handling authentication, HTTP&nbsp;response codes, and setting timeouts properly.&nbsp; Today I needed to change the code in TFS&nbsp;to better handle characters that are not allowed in XML. The problem is that if you have a method on your web service that takes [&hellip;]<\/p>\n","protected":false},"author":94,"featured_media":10268,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-10299","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"acf":[],"blog_post_summary":"<p>I&#8217;ve written&nbsp;about using the SoapHttpClientProtocol subclasses generated by wsdl.exe several times&nbsp;over the last year,&nbsp;including handling authentication, HTTP&nbsp;response codes, and setting timeouts properly.&nbsp; Today I needed to change the code in TFS&nbsp;to better handle characters that are not allowed in XML. The problem is that if you have a method on your web service that takes [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/buckh\/wp-json\/wp\/v2\/posts\/10299","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/buckh\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/buckh\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/buckh\/wp-json\/wp\/v2\/users\/94"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/buckh\/wp-json\/wp\/v2\/comments?post=10299"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/buckh\/wp-json\/wp\/v2\/posts\/10299\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/buckh\/wp-json\/wp\/v2\/media\/10268"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/buckh\/wp-json\/wp\/v2\/media?parent=10299"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/buckh\/wp-json\/wp\/v2\/categories?post=10299"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/buckh\/wp-json\/wp\/v2\/tags?post=10299"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}