Frage

Ich versuche, Probleme schießen ein Web-Service-Client in meinem aktuellen Projekt. Ich bin mir nicht sicher, ob die Plattform des Service-Servers (höchstwahrscheinlich LAMP). Ich glaube, es ist ein Fehler auf ihrer Seite des Zauns, wie ich die möglichen Probleme mit meinem Client beseitigt habe. Der Client ist ein Standard ASMX Typ Web-Referenz-Proxy-Auto aus dem Service-WSDL generierte.

Was ich bekommen muß, ist die RAW-SOAP-Nachrichten (Request und Antworten)

Was ist der beste Weg, um dies zu realisieren?

War es hilfreich?

Lösung

Ich habe in web.config folgenden Änderungen die SOAP (Request / Response) Umschlag zu bekommen. Dies wird ausgegeben gesamte rohe SOAP Informationen in die Datei 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>

Andere Tipps

Sie können eine Soapextension implementieren, die die vollständige Anforderung und Antwort in eine Protokolldatei protokolliert. Sie können dann die Soapextension in der web.config ermöglichen, die es leicht zu aktivieren / deaktivieren für Debug-Zwecke macht. Hier ist ein Beispiel, das ich gefunden habe, und modifiziert für den eigenen Gebrauch, in meinem Fall die Protokollierung von log4net getan wurde, aber Sie können die Log-Methoden mit Ihrem eigenen ersetzen.

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

Sie fügen Sie dann den folgenden Abschnitt in web.config wo YourNamespace und Yourassembly Punkt der Klasse und Montage Ihrer Soapextension:

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

Nicht sicher, warum die ganze Aufregung mit web.config oder Serializer Klasse. Der Code unten arbeitete für mich:

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

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

Versuchen Sie Fiddler2 es lassen Sie die Anfragen und Antwort zu überprüfen. Es könnte sich lohnen, unter Hinweis darauf, dass Fiddler mit arbeitet sowohl HTTP und HTTPS-Verkehr.

Es sieht aus wie Tim Carter-Lösung funktioniert nicht, wenn der Aufruf der Web-Referenz eine Ausnahme auslöst. Ich habe versucht, auf der Rohbahn resonse zu bekommen, damit ich es untersuchen kann (in Code) in der Fehlerbehandlung, wenn die Ausnahme ausgelöst wird. Ich finde jedoch, dass das Antwortprotokoll von Tims Methode geschrieben ist leer, wenn der Aufruf eine Ausnahme auslöst. Ich verstehe nicht ganz den Code, aber es scheint, dass Tims Methode schneidet in den Prozess nach dem Punkt, an das .Net bereits für ungültig erklärt und verworfen, die Web-Antwort.

Ich arbeite mit einem Client, einen Web-Service manuell mit niedrigem Niveau Codierung ist zu entwickeln. An diesem Punkt, sie fügen ihre eigenen internen Prozessfehlermeldungen als HTML-Nachrichten in die Antwort, bevor das SOAP-formatierte Antwort formatiert. Natürlich bläst die Automagic .Net Web-Referenz auf diese auf. Wenn ich an der rohen HTTP-Antwort erhalten konnte, nachdem eine Ausnahme ausgelöst wird, könnte ich für aussehen und jede SOAP-Antwort innerhalb der gemischten Rückkehr HTTP-Antwort analysieren und wissen, dass sie meine Daten OK oder nicht empfangen wird.

später ...

Hier ist eine Lösung, die funktioniert, auch nach einer execption (beachten Sie, dass ich erst nach der Antwort bin - könnte die Anfrage bekommen):

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

Hier ist, wie Sie es in der Konfigurationsdatei konfigurieren:

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

„TestCallWebService“ shoud mit dem Namen der Bibliothek ersetzt werden (das passierte der Name der Testkonsolenanwendung sein, ich arbeite in).

Sie sollten wirklich nicht haben Chain zu gehen; Sie sollten es besser in der Lage sein, einfach wie von Process zu tun:

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

Wenn Sie SoapMessage.Stream nachzuschlagen, es soll ein Read-Only sein streamen, dass Sie die Daten an dieser Stelle inspizieren können. Dies ist eine Ursache‘Schraube-up, wenn Sie den Strom nicht lesen, die nachfolgende Verarbeitung Bomben ohne Datenfehler gefunden (Strom am Ende war), und Sie können die Position zu Beginn nicht zurückgesetzt.

Interessanterweise, wenn Sie beide Methoden tun, die Chain und die Process Weise wird die Processmessage Methode funktioniert, weil Sie den Stream-Typ von connect geändert in Chain zu Memorystream und Memorysuchvorgänge zulässt. (Ich habe versucht, das zu connectMemory Gießen - wurde nicht erlaubt.)

So ..... sollte Microsoft entweder die Möglichkeit, Operationen auf dem Chain Typ suchen oder die SoapMessage.Stream wirklich eine schreibgeschützte Kopie machen, wie es angenommen hat, zu sein. (Schreiben Sie congressman, etc ...)

Ein weiterer Punkt. Nach einer Art und Weise der Erstellung der rohe HTTP-Antwort nach einer Ausnahme von retreive, ich habe immer noch nicht die vollständige Antwort (wie durch einen HTTP bestimmt Sniffer). Das lag daran, wenn die Entwicklung Web-Service, um die HTML-Fehlermeldungen zu Beginn der Reaktion hinzugefügt, hat es nicht die Content-Length-Header anpassen, so dass der Content-Length-Wert war kleiner als die Größe des tatsächlichen Antworttextes. Ich habe alle waren die Content-Length-Wert Anzahl der Zeichen - der Rest fehlte. Natürlich, wenn .Net den Antwortstream liest, liest nur die Content-Length Anzahl der Zeichen und erlaubt es nicht, für den Content-Length-Wert possibily falsch zu sein. Dies ist, wie es sein sollte; aber wenn der Content-Length-Header-Wert falsch ist, ist der einzige Weg, die Sie jemals bekommen die gesamte Antwort Körper ist mit einem HTTP-Sniffer (I Benutzer HTTP Analyzer von http://www.ieinspector.com ).

Ich ziehe es würde den Rahmen zu tun haben die Protokollierung für Sie in einem Protokollstrom Einhaken, die als Rahmen Prozesse protokolliert, die Strom zu Grunde liegen. Im Folgenden ist nicht so sauber, wie ich es möchte, da Sie nicht zwischen Anfrage und Antwort in der Chain Verfahren entscheiden kann. Im Folgenden ist, wie ich damit umgehen. Mit Dank an Jon Hanna für die zwingenden einen Strom Idee

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

Sie haben nicht angegeben, welche Sprache Sie verwenden, aber C # / .NET vorausgesetzt, Sie SOAP-Erweiterungen .

Verwenden Sie andernfalls einen Sniffer wie Wireshark

Ich weiß, ich bin ziemlich spät, um die Partei, und da die Sprache nicht angegeben tatsächlich wurde, ist hier eine Lösung VB.NET basierend auf Bimmerbound Antwort, falls jemand über diese zu stolpern passiert und braucht eine Lösung. . Hinweis: Sie einen Verweis auf die Stringbuilder-Klasse im Projekt haben müssen, wenn Sie nicht bereits

 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

Sie einfach die Funktion aufrufen, und es wird eine Zeichenkette mit der serialisierten XML des Objekts kehren Sie an den Webdienst zu passieren sind versucht (realistisch, sollte dies für jedes Objekt arbeiten Sie kümmern zu, um es zu werfen).

Als Randbemerkung, die ersetzen Anruf in der Funktion, bevor Sie die xml Rückkehr vom Ausgang zum Streifen aus vbCrLf Zeichen. Mein Zimmer hatte ein paar von ihnen innerhalb der generierten XML jedoch wird dies offensichtlich variieren je nachdem, was Sie versuchen, serialisiert werden und ich denke, sie könnten an den Webdienst während das Objekt gesendet gezupft werden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top