我正在调用一个Web服务。它接受Http Post。这是简单的XML over Http。您在请求正文中向它发送XML请求文档,您将在响应正文中返回XML响应文档。我在我们的代码库中有一个很好的库,这是我们多年来为请求和响应文档进行强类型序列化而构建的。一切正常。
现在,我正在集成的新服务并不总是在响应中发回相同的对象类型。在某些错误条件下,它返回一个特殊的错误文档。当然,在这些情况下,我的反序列化失败并且响应数据丢失。我知道我有一个反序列化错误,但由于响应丢失,我不知道根本原因是什么。
我认为问题在于GetResponseStream()返回的流不支持搜索操作,因此当我收到错误时,我不能简单地回滚流并重新读取数据并以不同方式处理它。
我正在寻找更好地解决这个问题的策略。
我认为我要做的是将响应流复制到可以寻找的内存流,然后再尝试反序列化。然后,如果发生错误,我可以回滚内存流并以不同方式处理响应数据。 https://stackoverflow.com/a/3434077/90236中有一个很好的例子。
有更好的方法吗?将响应流复制到内存流似乎有点浪费。
原始代码的简化版本:
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;
}
}
建议代码的简化版本:
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;
}
}
}
答案 0 :(得分:2)
如果返回的错误XML具有不同的根元素,则可以在实际反序列化之前使用XmlSerializer.CanDeserialize
方法。
答案 1 :(得分:1)
您可以做的是先将整个流作为字符串读取,然后根据需要反序列化/写入日志,而不是复制流。以下是代码段:
public string RetrieveResponse()
{
//Create a new http request
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create("http://example.com/service"); ;
Stream responseStream = null;
try
{
using (var httpResponse = (HttpWebResponse)httpRequest.GetResponse())
{
responseStream = httpResponse.GetResponseStream();
if (responseStream == null)
{
return null;
}
using (var streamRdr = new StreamReader(responseStream))
{
var response = streamRdr.ReadToEnd();
httpResponse.Close();
return response;
}
}
}
finally
{
if (responseStream != null)
{
responseStream.Dispose();
}
}
}
接下来,您可以使用以下代码段返回的响应反序列化:
private AccountResponse LoadFromString(string response)
{
if (string.IsNullOrEmpty(response))
return null;
try
{
AccountResponse result = null;
using (var stringReader = new StringReader(response))
{
using (XmlReader reader = new XmlTextReader(stringReader))
{
var serializer = new XmlSerializer(typeof(AccountResponse));
result = serializer.Deserialize(reader) as AccountResponse;
}
}
return result;
}
catch (Exception exception)
{
//Log the exception, etc.
}
}
PS:如果您知道服务返回的错误的格式,您实际上可以一次性将错误反序列化为对象,因此我们不必尝试捕获。希望这会有所帮助。