2008-11-19 2 views
77

Я пытаюсь устранить проблему с клиентом веб-службы в моем текущем проекте. Я не уверен в платформе Service Server (скорее всего, LAMP). Я считаю, что на их стороне забора есть вина, поскольку я устранил потенциальные проблемы с моим клиентом. Клиент является стандартным прокси-сервером типа ASMX, созданным с помощью WSDL-сервиса.Получение данных с мылом RAW из веб-справочного клиента, запущенного в ASP.net

Что мне нужно, чтобы добраться до это RAW SOAP сообщение (запрос и ответ)

Что такое лучший способ пойти по этому поводу?

ответ

118

Я сделал следующие изменения в web.config, чтобы получить SOAP (Запрос/ответ) Конверт. Это выведет всю необработанную информацию SOAP в файл trace.log.

<system.diagnostics> 
    <trace autoflush="true"/> 
    <sources> 
    <source name="System.Net" maxdatasize="1024"> 
     <listeners> 
     <add name="TraceFile"/> 
     </listeners> 
    </source> 
    <source name="System.Net.Sockets" maxdatasize="1024"> 
     <listeners> 
     <add name="TraceFile"/> 
     </listeners> 
    </source> 
    </sources> 
    <sharedListeners> 
    <add name="TraceFile" type="System.Diagnostics.TextWriterTraceListener" 
     initializeData="trace.log"/> 
    </sharedListeners> 
    <switches> 
    <add name="System.Net" value="Verbose"/> 
    <add name="System.Net.Sockets" value="Verbose"/> 
    </switches> 
</system.diagnostics> 
0

Вы не указали, какой язык вы используете, но при условии, что C#/.NET вы можете использовать SOAP extensions.

В противном случае, используйте сниффер, такие как Wireshark

+1

Да, пробовал проводку, он может отображать только информацию заголовка, содержимое мыла зашифровано. – 2008-11-19 22:08:27

+0

Возможно, потому, что вы используете https. Расширения SOAP, насколько я помню, работают на более высоком уровне, чтобы вы могли видеть данные после их дешифрования. – nzpcmad 2008-11-19 22:15:52

+2

Используйте Fiddler вместо Wireshark, он расшифровывает https из коробки. – 2013-02-05 09:18:39

18

Try Fiddler2 это позволит вам проверить запросы и ответ. Возможно, стоит отметить, что Fiddler работает как с трафиком http, так и с https.

+0

Это зашифрованная служба https – 2008-11-19 22:07:27

+8

Fiddler может дешифровать трафик https – 2008-11-20 16:03:45

+1

Я нашел это решение лучше, чем добавление трассировки в web/app.config. Благодаря! – andyuk 2009-09-18 09:06:52

30

Вы можете реализовать SoapExtension, который регистрирует полный запрос и ответ на файл журнала. Затем вы можете включить SoapExtension в файле web.config, что упрощает включение/выключение для целей отладки. Вот пример, который я нашел и модифицировал для моего собственного использования, в моем случае ведение журнала было выполнено с помощью log4net, но вы можете заменить методы журнала своим.

public class SoapLoggerExtension : SoapExtension 
{ 
    private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 
    private Stream oldStream; 
    private Stream newStream; 

    public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute) 
    { 
     return null; 
    } 

    public override object GetInitializer(Type serviceType) 
    { 
     return null; 
    } 

    public override void Initialize(object initializer) 
    { 

    } 

    public override System.IO.Stream ChainStream(System.IO.Stream stream) 
    { 
     oldStream = stream; 
     newStream = new MemoryStream(); 
     return newStream; 
    } 

    public override void ProcessMessage(SoapMessage message) 
    { 

     switch (message.Stage) 
     { 
      case SoapMessageStage.BeforeSerialize: 
       break; 
      case SoapMessageStage.AfterSerialize: 
       Log(message, "AfterSerialize"); 
        CopyStream(newStream, oldStream); 
        newStream.Position = 0; 
       break; 
       case SoapMessageStage.BeforeDeserialize: 
        CopyStream(oldStream, newStream); 
        Log(message, "BeforeDeserialize"); 
       break; 
      case SoapMessageStage.AfterDeserialize: 
       break; 
     } 
    } 

    public void Log(SoapMessage message, string stage) 
    { 

     newStream.Position = 0; 
     string contents = (message is SoapServerMessage) ? "SoapRequest " : "SoapResponse "; 
     contents += stage + ";"; 

     StreamReader reader = new StreamReader(newStream); 

     contents += reader.ReadToEnd(); 

     newStream.Position = 0; 

     log.Debug(contents); 
    } 

    void ReturnStream() 
    { 
     CopyAndReverse(newStream, oldStream); 
    } 

    void ReceiveStream() 
    { 
     CopyAndReverse(newStream, oldStream); 
    } 

    public void ReverseIncomingStream() 
    { 
     ReverseStream(newStream); 
    } 

    public void ReverseOutgoingStream() 
    { 
     ReverseStream(newStream); 
    } 

    public void ReverseStream(Stream stream) 
    { 
     TextReader tr = new StreamReader(stream); 
     string str = tr.ReadToEnd(); 
     char[] data = str.ToCharArray(); 
     Array.Reverse(data); 
     string strReversed = new string(data); 

     TextWriter tw = new StreamWriter(stream); 
     stream.Position = 0; 
     tw.Write(strReversed); 
     tw.Flush(); 
    } 
    void CopyAndReverse(Stream from, Stream to) 
    { 
     TextReader tr = new StreamReader(from); 
     TextWriter tw = new StreamWriter(to); 

     string str = tr.ReadToEnd(); 
     char[] data = str.ToCharArray(); 
     Array.Reverse(data); 
     string strReversed = new string(data); 
     tw.Write(strReversed); 
     tw.Flush(); 
    } 

    private void CopyStream(Stream fromStream, Stream toStream) 
    { 
     try 
     { 
      StreamReader sr = new StreamReader(fromStream); 
      StreamWriter sw = new StreamWriter(toStream); 
      sw.WriteLine(sr.ReadToEnd()); 
      sw.Flush(); 
     } 
     catch (Exception ex) 
     { 
      string message = String.Format("CopyStream failed because: {0}", ex.Message); 
      log.Error(message, ex); 
     } 
    } 
} 

[AttributeUsage(AttributeTargets.Method)] 
public class SoapLoggerExtensionAttribute : SoapExtensionAttribute 
{ 
    private int priority = 1; 

    public override int Priority 
    { 
     get { return priority; } 
     set { priority = value; } 
    } 

    public override System.Type ExtensionType 
    { 
     get { return typeof (SoapLoggerExtension); } 
    } 
} 

Вы затем добавьте следующую секцию в web.config, где YourNamespace и YourAssembly точка класса и сборки вашего SoapExtension:

<webServices> 
    <soapExtensionTypes> 
    <add type="YourNamespace.SoapLoggerExtension, YourAssembly" 
     priority="1" group="0" /> 
    </soapExtensionTypes> 
</webServices> 
1

Я бы предпочел, чтобы структура выполняла регистрацию для вас, подключая ведомый поток, который регистрируется как инфраструктура, обрабатывающая этот поток. Следующее не так чисто, как хотелось бы, так как вы не можете выбирать между запросом и ответом в методе ChainStream. Вот как я справляюсь с этим. С благодарностью Джоном Ханна для переопределения идеи потока

public class LoggerSoapExtension : SoapExtension 
{ 
    private static readonly string LOG_DIRECTORY = ConfigurationManager.AppSettings["LOG_DIRECTORY"]; 
    private LogStream _logger; 

    public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute) 
    { 
     return null; 
    } 
    public override object GetInitializer(Type serviceType) 
    { 
     return null; 
    } 
    public override void Initialize(object initializer) 
    { 
    } 
    public override System.IO.Stream ChainStream(System.IO.Stream stream) 
    { 
     _logger = new LogStream(stream); 
     return _logger; 
    } 
    public override void ProcessMessage(SoapMessage message) 
    { 
     if (LOG_DIRECTORY != null) 
     { 
      switch (message.Stage) 
      { 
       case SoapMessageStage.BeforeSerialize: 
        _logger.Type = "request"; 
        break; 
       case SoapMessageStage.AfterSerialize: 
        break; 
       case SoapMessageStage.BeforeDeserialize: 
        _logger.Type = "response"; 
        break; 
       case SoapMessageStage.AfterDeserialize: 
        break; 
      } 
     } 
    } 
    internal class LogStream : Stream 
    { 
     private Stream _source; 
     private Stream _log; 
     private bool _logSetup; 
     private string _type; 

     public LogStream(Stream source) 
     { 
      _source = source; 
     } 
     internal string Type 
     { 
      set { _type = value; } 
     } 
     private Stream Logger 
     { 
      get 
      { 
       if (!_logSetup) 
       { 
        if (LOG_DIRECTORY != null) 
        { 
         try 
         { 
          DateTime now = DateTime.Now; 
          string folder = LOG_DIRECTORY + now.ToString("yyyyMMdd"); 
          string subfolder = folder + "\\" + now.ToString("HH"); 
          string client = System.Web.HttpContext.Current != null && System.Web.HttpContext.Current.Request != null && System.Web.HttpContext.Current.Request.UserHostAddress != null ? System.Web.HttpContext.Current.Request.UserHostAddress : string.Empty; 
          string ticks = now.ToString("yyyyMMdd'T'HHmmss.fffffff"); 
          if (!Directory.Exists(folder)) 
           Directory.CreateDirectory(folder); 
          if (!Directory.Exists(subfolder)) 
           Directory.CreateDirectory(subfolder); 
          _log = new FileStream(new System.Text.StringBuilder(subfolder).Append('\\').Append(client).Append('_').Append(ticks).Append('_').Append(_type).Append(".xml").ToString(), FileMode.Create); 
         } 
         catch 
         { 
          _log = null; 
         } 
        } 
        _logSetup = true; 
       } 
       return _log; 
      } 
     } 
     public override bool CanRead 
     { 
      get 
      { 
       return _source.CanRead; 
      } 
     } 
     public override bool CanSeek 
     { 
      get 
      { 
       return _source.CanSeek; 
      } 
     } 

     public override bool CanWrite 
     { 
      get 
      { 
       return _source.CanWrite; 
      } 
     } 

     public override long Length 
     { 
      get 
      { 
       return _source.Length; 
      } 
     } 

     public override long Position 
     { 
      get 
      { 
       return _source.Position; 
      } 
      set 
      { 
       _source.Position = value; 
      } 
     } 

     public override void Flush() 
     { 
      _source.Flush(); 
      if (Logger != null) 
       Logger.Flush(); 
     } 

     public override long Seek(long offset, SeekOrigin origin) 
     { 
      return _source.Seek(offset, origin); 
     } 

     public override void SetLength(long value) 
     { 
      _source.SetLength(value); 
     } 

     public override int Read(byte[] buffer, int offset, int count) 
     { 
      count = _source.Read(buffer, offset, count); 
      if (Logger != null) 
       Logger.Write(buffer, offset, count); 
      return count; 
     } 

     public override void Write(byte[] buffer, int offset, int count) 
     { 
      _source.Write(buffer, offset, count); 
      if (Logger != null) 
       Logger.Write(buffer, offset, count); 
     } 
     public override int ReadByte() 
     { 
      int ret = _source.ReadByte(); 
      if (ret != -1 && Logger != null) 
       Logger.WriteByte((byte)ret); 
      return ret; 
     } 
     public override void Close() 
     { 
      _source.Close(); 
      if (Logger != null) 
       Logger.Close(); 
      base.Close(); 
     } 
     public override int ReadTimeout 
     { 
      get { return _source.ReadTimeout; } 
      set { _source.ReadTimeout = value; } 
     } 
     public override int WriteTimeout 
     { 
      get { return _source.WriteTimeout; } 
      set { _source.WriteTimeout = value; } 
     } 
    } 
} 
[AttributeUsage(AttributeTargets.Method)] 
public class LoggerSoapExtensionAttribute : SoapExtensionAttribute 
{ 
    private int priority = 1; 
    public override int Priority 
    { 
     get 
     { 
      return priority; 
     } 
     set 
     { 
      priority = value; 
     } 
    } 
    public override System.Type ExtensionType 
    { 
     get 
     { 
      return typeof(LoggerSoapExtension); 
     } 
    } 
} 
5

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

Я работаю с клиентом, который разрабатывает веб-сервис вручную с низким уровнем кодирования. На этом этапе они добавляют свои собственные сообщения об ошибках внутреннего процесса в сообщениях в формате HTML в ответ перед ответом в формате SOAP. Конечно, автоматическая веб-ссылка .Net взрывается. Если бы я мог получить необработанный HTTP-ответ после того, как генерируется исключение, я мог бы искать и анализировать любой ответ SOAP в смешанном ответе HTTP-ответа и знать, что они получили мои данные в порядке или нет.

Позже ...

Вот решение, которое делает работу, даже после execption (обратите внимание, что я только после ответа - может получить запрос тоже):

namespace ChuckBevitt 
{ 
    class GetRawResponseSoapExtension : SoapExtension 
    { 
     //must override these three methods 
     public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute) 
     { 
      return null; 
     } 
     public override object GetInitializer(Type serviceType) 
     { 
      return null; 
     } 
     public override void Initialize(object initializer) 
     { 
     } 

     private bool IsResponse = false; 

     public override void ProcessMessage(SoapMessage message) 
     { 
      //Note that ProcessMessage gets called AFTER ChainStream. 
      //That's why I'm looking for AfterSerialize, rather than BeforeDeserialize 
      if (message.Stage == SoapMessageStage.AfterSerialize) 
       IsResponse = true; 
      else 
       IsResponse = false; 
     } 

     public override Stream ChainStream(Stream stream) 
     { 
      if (IsResponse) 
      { 
       StreamReader sr = new StreamReader(stream); 
       string response = sr.ReadToEnd(); 
       sr.Close(); 
       sr.Dispose(); 

       File.WriteAllText(@"C:\test.txt", response); 

       byte[] ResponseBytes = Encoding.ASCII.GetBytes(response); 
       MemoryStream ms = new MemoryStream(ResponseBytes); 
       return ms; 

      } 
      else 
       return stream; 
     } 
    } 
} 

Вот как настроить его в конфигурационном файле:

<configuration> 
    ... 
    <system.web> 
    <webServices> 
     <soapExtensionTypes> 
     <add type="ChuckBevitt.GetRawResponseSoapExtension, TestCallWebService" 
      priority="1" group="0" /> 
     </soapExtensionTypes> 
    </webServices> 
    </system.web> 
</configuration> 

«TestCallWebService» Шоуда быть заменено на имя библиотеки (что случилось быть имя приложения тест консоли я работал в).

Вам действительно не нужно ехать в ChainStream; Вы должны быть в состоянии сделать это более просто из ProcessMessage как:

public override void ProcessMessage(SoapMessage message) 
{ 
    if (message.Stage == SoapMessageStage.BeforeDeserialize) 
    { 
     StreamReader sr = new StreamReader(message.Stream); 
     File.WriteAllText(@"C:\test.txt", sr.ReadToEnd()); 
     message.Stream.Position = 0; //Will blow up 'cause type of stream ("ConnectStream") doesn't alow seek so can't reset position 
    } 
} 

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

Интересно, что если вы используете оба метода, пути ChainStream и ProcessMessage, метод ProcessMessage будет работать, потому что вы изменили тип потока из ConnectStream на MemoryStream в ChainStream, а MemoryStream позволяет выполнять операции поиска. (Я попробовал лить ConnectStream в MemoryStream - не разрешался.)

Итак ..... Microsoft должна либо разрешить операции поиска по типу ChainStream, либо сделать SoapMessage.Stream действительно копией только для чтения, как предполагается быть. (Напишите своего конгрессмена и т. Д.)

Еще один пункт. После создания способа отвлечения необработанного HTTP-ответа после исключения я все равно не получил полный ответ (как определено с помощью сниффера HTTP). Это было связано с тем, что, когда веб-служба разработки добавила сообщения об ошибках HTML в начало ответа, она не корректировала заголовок Content-Length, поэтому значение Content-Length было меньше размера фактического тела ответа. Все, что у меня было, это количество символов Content-Length символов - остальные отсутствовали. Очевидно, что когда .Net считывает поток ответов, он просто считывает количество символов Content-Length и не допускает, чтобы значение Content-Length было возможно неправильным. Это так и должно быть; но если значение заголовка Content-Length неверно, единственный способ получения всего тела ответа - с помощью сниффера HTTP (пользовательский HTTP-анализатор пользователя от http://www.ieinspector.com).

24

Не знаете, почему все суеты с web.config или классом сериализатора. Ниже код работает для меня:

XmlSerializer xmlSerializer = new XmlSerializer(myEnvelope.GetType()); 

using (StringWriter textWriter = new StringWriter()) 
{ 
    xmlSerializer.Serialize(textWriter, myEnvelope); 
    return textWriter.ToString(); 
} 
0

Я понимаю, что я довольно поздно к партии, и с тех пор язык не был фактически определен, вот решение VB.NET на основе ответа Bimmerbound, в в случае, если кто случается споткнуться через это и нуждается в решении. Примечание: вам нужно иметь ссылку на класс stringbuilder в вашем проекте, если вы этого еще не сделали.

Shared Function returnSerializedXML(ByVal obj As Object) As String 
    Dim xmlSerializer As New System.Xml.Serialization.XmlSerializer(obj.GetType()) 
    Dim xmlSb As New StringBuilder 
    Using textWriter As New IO.StringWriter(xmlSb) 
     xmlSerializer.Serialize(textWriter, obj) 
    End Using 


    returnSerializedXML = xmlSb.ToString().Replace(vbCrLf, "") 

End Function 

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

В качестве примечания, вызов замены в функции перед возвратом xml состоит в том, чтобы вырезать символы vbCrLf из вывода. У меня было множество из них в сгенерированном xml, но это, очевидно, будет меняться в зависимости от того, что вы пытаетесь сериализовать, и я думаю, что они могут быть удалены во время отправки объекта в веб-службу.