3

웹 서비스를 호출하고 있습니다. 그것은 Http Post를 허용합니다. 그것은 HTTP를 통해 간단한 XML입니다. 요청 본문에 XML 요청 문서를 보내면 응답 본문에 XML 응답 문서가 반환됩니다. 필자는 수년 동안 요청 및 응답 문서 모두에 대해 강력하게 형식화 된 serialization을 작성한 코드 기반의 멋진 라이브러리를 보유하고 있습니다. 모든 것이 정상적으로 작동합니다.다른 형식의 HttpWebResponse GetResponseStream에서 XML 비 직렬화

이제 내가 통합 할 새 서비스가 항상 응답에서 동일한 객체 유형을 다시 보내는 것은 아닙니다. 특정 오류 조건에서 특수 오류 문서를 반환합니다. 물론 이러한 상황에서 내 deserialization이 실패하고 응답 데이터가 손실됩니다. deserialization errror가 있다는 것을 알고 있지만 응답이 없어지기 때문에 근본 원인이 무엇인지 알 수 없습니다.

GetResponseStream()에 의해 반환 된 스트림이 탐색 작업을 지원하지 않는다고 생각합니다. 그래서 오류가 발생하면 단순히 스트림을 되감거나 데이터를 다시 읽고 다르게 처리 할 수 ​​없습니다.

나는 이것을 더 잘 다루는 전략을 찾고있다.

제가 생각하기에 나는 deserialize하기 전에 seekable 인 메모리 스트림에 응답 스트림을 복사하는 것이라고 생각합니다. 그런 다음 오류가 발생하면 메모리 스트림을 되감기하고 응답 데이터를 다르게 처리 할 수 ​​있습니다. https://stackoverflow.com/a/3434077/90236에 좋은 예가있었습니다.

더 좋은 방법이 있나요? 응답 스트림을 메모리 스트림에 복사하는 것은 낭비입니다. 원래 코드의 versioin 단순화

:

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 메서드를 사용할 수 있습니다.

+0

감사합니다. 불행히도 CanDeserialize 테스트를 추가하는 것이 매우 어렵습니다. 그래서 지금은 스트림 복사본 접근 방식을 유지할 것입니다. 그러나 이것이 올바른 대답이라고 생각합니다. –

1

대신의 흐름을 복사하는 것입니다 먼저 문자열로 입력 한 다음 필요에 따라 로그를 직렬화/쓰기하십시오. 다음 코드 조각은 다음과 같습니다

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 :이 서비스에 의해 반환 된 오류의 형식을 알고있는 경우에, 당신이

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


      } 
     } 

다음, 아래의 코드 조각에 의해 반환 된 응답을 사용하여 직렬화 할 수 있습니다 실제로 한 번에 객체에 대한 오류를 역 직렬화 할 수 있으므로 시도하고 잡을 필요가 없습니다. 희망이 도움이됩니다.