3

我正在调用网络服务。它接受 Http Post。它是基于 Http 的简单 XML。您在请求正文中向它发送一个 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;
      }
    }
}
4

2 回答 2

2

如果返回的错误 XML 具有不同的根元素,您可以XmlSerializer.CanDeserialize在实际反序列化之前使用该方法。

于 2014-02-13T09:00:21.733 回答
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:如果你知道服务返回的错误的格​​式,其实你也可以一次性将错误反序列化为一个对象,这样我们就不用去尝试捕捉了。希望这可以帮助。

于 2014-02-13T04:28:00.887 回答