views:

615

answers:

3

Lately, we've been seeing exceptions like this in our .NET (.asmx) webservices:

System.Web.Services.Protocols.SoapException: Server was unable to read request. ---> System.InvalidOperationException: There is an error in XML document (868, -3932). ---> System.Xml.XmlException: '.', hexadecimal value 0x00, is an invalid character. Line 868, position -3932.
   at System.Xml.XmlTextReaderImpl.Throw(Exception e)
   at System.Xml.XmlTextReaderImpl.Throw(String res, String[] args)
   at System.Xml.XmlTextReaderImpl.Throw(Int32 pos, String res, String[] args)
   at System.Xml.XmlTextReaderImpl.ThrowInvalidChar(Int32 pos, Char invChar)
   at System.Xml.XmlTextReaderImpl.ParseNumericCharRefInline(Int32 startPos, Boolean expand, BufferBuilder internalSubsetBuilder, Int32& charCount, EntityType& entityType)
   at System.Xml.XmlTextReaderImpl.ParseText(Int32& startPos, Int32& endPos, Int32& outOrChars)
   at System.Xml.XmlTextReaderImpl.ParseText()
   at System.Xml.XmlTextReaderImpl.ParseElementContent()
   at System.Xml.XmlTextReaderImpl.Read()
   at System.Xml.XmlTextReader.Read()
   at System.Web.Services.Protocols.SoapServerProtocol.SoapEnvelopeReader.Read()
   at System.Xml.XmlReader.ReadElementString()
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReader1.Read14_SendErrlog()
   at Microsoft.Xml.Serialization.GeneratedAssembly.ArrayOfObjectSerializer12.Deserialize(XmlSerializationReader reader)
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events)
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events)
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle)
   at System.Web.Services.Protocols.SoapServerProtocol.ReadParameters()
   --- End of inner exception stack trace ---
   at System.Web.Services.Protocols.SoapServerProtocol.ReadParameters()
   at System.Web.Services.Protocols.WebServiceHandler.CoreProcessRequest()

How can I debug this exception? This exception is getting reported to us from a SOAP filter which looks for exceptions in message.Stage = SoapMessageStage.AfterSerialize.

Is there any way to get at the original soap request? How do I get an invalid character at line 868, column -3932? How can there a negative column 3932?

+1  A: 

You should be able to get the original message by using another SoapExtension. In fact, the same extension could probably be modified to make a copy of the input, and to discard it if there is no exception. The original input would then be available to you if an exception occurred.

You could also use an external tool like Fiddler to watch what's being sent to you.

John Saunders
So, basically copy it in message.Stage = SoapMessageStage.BeforeDeserialize? Can I guarantee that the first AfterDeserialize for message A comes immediately after A's BeforeDeserialize?
Bob King
I would also like to know the answer to Bob King's question; this was the approach I had come up with on my own, and went to writing the SoapExtension, and realized I couldn't think of a way to do it thread-safely. :(
Grank
Sorry, I didn't answer. The answer is: if the documentation doesn't provide a guarantee, then there is no guarantee.
John Saunders
A: 

FYI: SoapException.Message is intentionally left vague to prevent exposing too much information which could potentially be used to exploit the system.

For your particular case I'd take John's advice and install Fiddler to monitor the actual HTTP traffic and view the message on the wire.

The portion of your exception that jumps out at me is the "hexadecimal value 0x00, is an invalid character" but as you mentioned the line number it points to is bunk--so it's nothing concrete.

What kind of parameters do you pass to the service? Are you doing any kind of custom encoding with a SOAP extension? Any additional SOAP headers being added?

STW
+2  A: 

This is one of the irritating things about the Microsoft web services approach -- if the request cannot be deserialized into the objects in your web method signature then the service consumer gets a cryptic message. And to top it off, the request never makes it into your web service because it cannot be deserialzied so you can't handle the error gracefully.

What I would do to help with these types of issues is to create a new SoapExtension that simply lets you output the raw XML to a destination that is convenient for you (file or Trace to be read by DebugView or whatever else you like). The code would go in the BeforeDeserialize stage. You could enable the SoapExtension via web.config in the event you wanted to investigate one of these issues. The downside of using the web.config to add the SoapExtension is that it will be active for the entire web application. You could add some additional custom configuration that would allow your service to only log information for a specific endpoint or a specific web method if you wanted.

Usually, just by seeing the incoming XML you can see what the problem is. If not, then you could try to manually run the captured XML through small program that invokes the XML serializer and see if you can find out what is going on. Another useful tool is Web Service Studio 2 which is a test harness which lets you enter data and invoke your service (and also submit any XML you want).

In terms of your specific issue, here is my take/guess. It looks like ASCII character null is getting encoded and sent to your service which is invalid according to the XML Spec. The simple answer is not to send that character. But who is sending that character? Is it a .NET client? Do you have control over the client? If you need to work around someone else's bug, then you may have to replace the offending character(s) with another character (perhaps empty string).

Tuzo
Our app is the client and it's just sending up some xml-literal escaped text (-)
Bob King