Десериализация XML из HttpWebResponse GetResponseStream с различными типами

Я звоню в веб-службу. Он принимает Http Post. Это простой XML поверх Http. Вы отправляете ему документ запроса XML в теле запроса, вы возвращаете документ ответа XML в теле ответа. У меня есть хорошая библиотека в нашей кодовой базе, которую мы создали за эти годы, которая строго типизировала сериализацию для документов запроса и ответа. Все нормально работает нормально.

Теперь новый сервис, с которым я интегрируюсь, не всегда отправляет обратно тот же тип объекта в ответе. При определенных условиях ошибки он возвращает специальный документ об ошибке. Конечно, в этих ситуациях моя десериализация завершается неудачно и данные ответа теряются. Я знаю, что у меня ошибка десериализации, но так как ответ потерян, я не знаю, в чем была основная причина.

Я думаю, что проблема в том, что поток, возвращаемый GetResponseStream(), не поддерживает операции поиска, поэтому, когда я получаю ошибку, я не могу просто перемотать поток и перечитать данные и обработать их по-другому.

Я ищу стратегии, чтобы лучше справиться с этим.

Я думаю, что собираюсь скопировать поток ответа в поток памяти, который можно найти, прежде чем я попытаюсь десериализовать. Затем, если произойдет ошибка, я могу перемотать поток памяти и обработать данные ответа по-разному. Был хороший пример в /questions/42512700/oshibka-etot-potok-ne-podderzhivaet-operatsii-poiska-v-c/42512704#42512704.

Есть лучший способ сделать это? Кажется, что расточительно копировать поток ответов в поток памяти.

Упрощенная версия исходного кода:

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;
      }
    }
}

2 ответа

Решение

Если возвращенная ошибка XML имеет другой корневой элемент, вы можете использовать XmlSerializer.CanDeserialize Метод перед фактической десериализацией.

Вместо того, чтобы копировать поток, вы могли бы сначала прочитать весь поток как строку, а затем десериализовать / записать журналы по мере необходимости. Ниже приведены фрагменты кода:

 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: если вам известен формат ошибки, возвращаемой службой, вы фактически можете десериализовать ошибку и для объекта за один раз, поэтому нам не нужно пытаться ее отлавливать. Надеюсь это поможет.

Другие вопросы по тегам