I am calling a web service. It accepts Http Post. It is simple XML over Http. You send it an XML request document in the body of the request, you get back an XML response document in the body of the response. I have a nice library in our code base that that we've built over the years that does strongly typed serialization for both the request and response documents. Everything normally works fine.
Now, the new service I'm integrating with doesn't always send back the same object type in the response. Under certain error conditions it returns a special error document. Of course, in these situations my deserialization fails and the response data is lost. I know that I have a deserialization errror, but since the response is lost, I don't know what the root cause was.
I think the problem is that the stream returned by GetResponseStream() does not support seek operations, so when I get an error I cannot simply rewind the stream and re-read the data and handle it differently.
I'm looking for strategies to better handle this.
What I think I'm going to do is copy the response stream to a memory stream which is seekable before I attempt to deserialize. Then, if an error occurs I can rewind the memory stream and handle the response data differently. There was a good example in https://stackoverflow.com/a/3434077/90236.
Is there a better way to do this? It seems kind of wasteful to copy the response stream to a memory stream.
Simplified versioin of original code:
AccountRequest requestVal = new AccountRequest();
// initialize requestVal object
var request = (HttpWebRequest)WebRequest.Create("http://example.com/service");
request.Method = "POST";
request.ContentType = "text/xml";
using (Stream webStream = request.GetRequestStream())
{
var serializer = new XmlSerializer(typeof(AccountRequest));
serializer.Serialize(webStream, requestVal);
webStream.Flush();
webStream.Close();
}
AccountResponse returnVal;
using (var response = (HttpWebResponse)request.GetResponse())
{
Stream responseStream = response.GetResponseStream();
var serializer = new XmlSerializer(typeof(responseStream));
try
{
returnVal = (AccountResponse)serializer.Deserialize(responseStream);
}
catch (Exception ex)
{
// if an exception occurs, the response stream data is lost.
// The responseStream is not seekable.
logger.ErrorFormat("After Exception:\n{0}", ex.ToString());
throw;
}
}
Simplified version of proposed code:
AccountRequest requestVal = new AccountRequest();
// initialize requestVal object
var request = (HttpWebRequest)WebRequest.Create("http://example.com/service");
request.Method = "POST";
request.ContentType = "text/xml";
using (Stream webStream = request.GetRequestStream())
{
var serializer = new XmlSerializer(typeof(AccountRequest));
serializer.Serialize(webStream, requestVal);
webStream.Flush();
webStream.Close();
}
AccountResponse returnVal;
using (var response = (HttpWebResponse)request.GetResponse())
{
Stream responseStream = response.GetResponseStream();
using (MemoryStream ms = new MemoryStream())
{
// copy response stream to a seekable memorystream
int count = 0;
do
{
byte[] buf = new byte[1024];
count = responseStream.Read(buf, 0, 1024);
ms.Write(buf, 0, count);
} while (responseStream.CanRead && count > 0);
ms.Position = 0;
// now attempt to desrialize from the memory stream
var serializer = new XmlSerializer(typeof(AccountResponse));
try
{
returnVal = (AccountResponse)serializer.Deserialize(ms);
}
catch (Exception ex)
{
// if an exception occured, rewind the stream and write an error to the log
ms.Position = 0;
using (var reader = new StreamReader(ms, Encoding.UTF8))
{
logger.ErrorFormat("After Exception:\n{0}\n\nRespons:\n{1}",
ex.ToString(), reader.ReadToEnd());
}
throw;
}
}
}
If the returned error XML has a different root element you can use the XmlSerializer.CanDeserialize
method before actually deserializing.