Pergunta

Eu estou tentando atirar problemas um cliente de serviço web no meu projeto atual. Eu não tenho certeza da plataforma do Server Service (Muito provavelmente LAMP). Eu acredito que há uma falha em seu lado da cerca como eu ter eliminado os possíveis problemas com o meu cliente. O cliente é um padrão tipo ASMX automática de proxy referência web gerado a partir do WSDL serviço.

O que eu preciso para chegar é o Mensagens RAW de sabão (solicitação e as respostas)

O que é a melhor maneira de fazer isso?

Foi útil?

Solução

Eu fiz seguir mudanças na web.config para pegar o sabonete (Request / Response) Envelope. Esta saída vontade de todas as informações do sabão cru ao trace.log arquivo.

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

Outras dicas

Você pode implementar um SoapExtension que registra o pedido completo e resposta a um arquivo de log. Você pode então permitir que o SoapExtension no web.config, o que torna fácil de ligar / desligar para fins de depuração. Aqui está um exemplo que eu encontrei e modificadas para meu próprio uso, no meu caso o registro foi feito por log4net, mas você pode substituir os métodos de log com o seu próprio.

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

Em seguida, adicione a seguinte seção ao seu web.config onde YourNamespace e YourAssembly ponto para a classe e montagem de seu SoapExtension:

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

Não sei por que todo o alarido com web.config ou uma classe serializador. O código a seguir trabalhou para mim:

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

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

Tente Fiddler2 ele vai deixar você inspecionar as solicitações e respostas. Pode ser interessante notar que Fiddler funciona tanto com tráfego HTTP e HTTPS.

Parece que a solução da Tim Carter não funciona se a chamada para a referência da web lança uma exceção. Eu tenho tentado chegar à resonse web matéria para que eu possa examiná-lo (em código) no manipulador de erro uma vez que a exceção é lançada. No entanto, eu estou achando que o registo de resposta por escrito pelo método de Tim está em branco quando a chamada lança uma exceção. Eu não entendo completamente o código, mas parece que de Tim método cortes no processo após o ponto onde .Net já invalidado e descartou a resposta web.

Eu estou trabalhando com um cliente que está desenvolvendo um serviço web manualmente com baixo nível de codificação. Neste ponto, eles estão adicionando suas próprias mensagens de erro processo interno como HTML formatado mensagens para a resposta antes da resposta SOAP formatada. Claro, a referência da web automagic .Net explode sobre este assunto. Se eu pudesse chegar à resposta HTTP cru, após uma exceção é lançada, eu poderia procurar e analisar qualquer resposta SOAP dentro da resposta HTTP retornar mista e saber que eles receberam os meus dados OK ou não.

Mais tarde ...

Aqui está uma solução que funciona, mesmo depois de um execption (note que eu sou somente após a resposta - poderia obter o pedido também):

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

Veja como você configurá-lo no arquivo de configuração:

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

"TestCallWebService" deviam ser substituído com o nome da biblioteca (que passou a ser o nome do console app teste eu estava trabalhando em).

Você realmente não deveria ter que ir para ChainStream; você deve ser capaz de fazê-lo de forma mais simples de ProcessMessage como:

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

Se você olhar para cima SoapMessage.Stream, que é suposto ser um read-only fluxo que você pode usar para inspecionar os dados neste momento. Esta é uma 'causa parafuso-up se você ler o fluxo, bombas de processamento subsequentes sem dados encontrados erros (stream estava no fim) e você não pode redefinir a posição para o início.

Curiosamente, se você fizer ambos os métodos, o ChainStream e as formas ProcessMessage, o método ProcessMessage vai funcionar porque você mudou o tipo de fluxo de ConnectStream para MemoryStream em ChainStream e MemoryStream não permite operações de busca. (I tentou lançar o ConnectStream para MemoryStream -. Não foi permitir)

Assim ..... Microsoft deve ou permitem operações de busca do tipo ChainStream ou fazer o SoapMessage.Stream verdadeiramente um read-only copiar como é suposto ser. (Escreva seus congressista, etc ...)

Um outro ponto. Depois de criar um caminho para recuperar a resposta HTTP bruta após uma exceção, eu ainda não recebi a resposta completa (conforme determinado por um HTTP sniffer). Este foi porque quando o serviço de desenvolvimento web adicionado as mensagens de erro HTML para o início da resposta, não ajustar o cabeçalho Content-Length, então o valor Content-Length foi menor do que o tamanho do corpo de resposta real. Tudo o que foi o número valor Content-Length de personagens - o resto estavam faltando. Obviamente, quando .Net lê o fluxo de resposta, ele apenas lê o número de Content-Length de caracteres e não permite o valor Content-Length possibily estar errado. Isto é como deveria ser; mas se o valor do cabeçalho Content-Length está errado, a única maneira que você jamais vai conseguir todo o corpo da resposta é com um HTTP sniffer (usuário I HTTP Analyzer a partir de http://www.ieinspector.com ).

Eu preferiria ter o enquadramento fazer o registro para você enganchando em um fluxo de log que registros como os processos de enquadramento que subjacente fluxo. O seguinte não é tão limpo como eu gostaria que, desde que você não pode decidir entre o pedido ea resposta no método ChainStream. O seguinte é como eu lidar com isso. Com agradecimentos a Jon Hanna para a substituir uma idéia corrente

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

Você não especificou o idioma que você está usando, mas assumindo C # / NET você poderia usar extensões SOAP .

Caso contrário, use um sniffer como Wireshark

Eu percebo que estou muito atrasado para a festa, e uma vez que a língua não foi realmente especificado, aqui está uma solução VB.NET com base na resposta do Bimmerbound, no caso de alguém acontece tropeçar este e precisa de uma solução. Nota:. Você precisa ter uma referência para a classe stringbuilder em seu projeto, se você já não

 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

Basta chamar a função e ele irá retornar uma string com o XML serializado do objeto que você está tentando passar para o serviço web (realisticamente, isso deve funcionar para qualquer objeto que você gostaria de jogar nele também).

Como uma nota lateral, a chamada substituir na função antes de retornar o xml é tirar fora caracteres vbCrLf a partir da saída. O meu tinha um monte deles dentro do XML gerado no entanto, isso irá, obviamente, variam de acordo com o que você está tentando serialize, e eu acho que eles podem ser retirados durante o objeto que está sendo enviada para o serviço web.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top