الحصول على RAW البيانات الصابون من عميل مرجع ويب تعمل في ASP.net

StackOverflow https://stackoverflow.com/questions/300674

سؤال

وأنا أحاول أن المتاعب اطلاق النار على عميل خدمة الإنترنت في مشروعي الحالي. لست متأكدا من منصة لخدمة ملقم (على الأرجح LAMP). أعتقد أن هناك خطأ من جانبهم من السياج كما قلت القضاء على المشاكل المحتملة مع موكلي. العميل هو معيار نوع ASMX وكيل المرجعية على شبكة الإنترنت السيارات الناتجة عن WSDL الخدمة.

وماذا أحتاج للوصول الى هو رسائل الخام SOAP (طلب والردود)

ما هي أفضل طريقة للذهاب نحو هذا؟

هل كانت مفيدة؟

المحلول

ولقد تقدمت التالية التغييرات في web.config للحصول على الصابون (طلب / الاستجابة) مغلف. هذا سوف يظهر كافة المعلومات 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>

نصائح أخرى

ويمكنك تنفيذ 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>

ولست متأكدا لماذا كل هذه الضجة مع الملف web.config أو فئة مسلسل. عملت رمز أدناه بالنسبة لي:

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

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

Fiddler2 وسوف تتيح لك فحص الطلبات والاستجابة لها. قد يكون من الجدير بالذكر أن عازف الكمان تعمل مع كل من HTTP و HTTPS حركة المرور.

ويبدو ان لا يعمل الحل تيم كارتر اذا كانت الدعوة إلى الإشارة الويب رميات استثناء. لقد تم في محاولة للحصول على resonse الويب الخام حتى أتمكن من النظر فيه (في التعليمات البرمجية) في معالج الخطأ مرة واحدة يتم طرح استثناء. ومع ذلك، أنا وجدت أن سجل ردا مكتوبا بطريقة تيم فارغ عندما يلقي الدعوة استثناء. أنا لا أفهم تماما رمز، ولكن يبدو أن التخفيضات طريقة تيم في العملية بعد نقطة حيث صافي ويبطل بالفعل، وتجاهل الرد على شبكة الإنترنت.

وأنا أعمل مع العميل الذي تم تطوير خدمة الويب يدويا مع انخفاض مستوى الترميز. عند هذه النقطة، فإنها تقوم بإضافة رسائل الخطأ العملية الداخلية الخاصة بها كما HTML تنسيق الرسائل إلى الاستجابة قبل الاستجابة SOAP المنسقة. بطبيعة الحال، فإن automagic صافي إشارة الشبكة ينفجر في هذا الشأن. إن كنت أستطيع الحصول على استجابة 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 - لم تسمح.)

وهكذا ..... مايكروسوفت يجب إما السماح تسعى العمليات على نوع ChainStream أو جعل SoapMessage.Stream حقا للقراءة فقط نسخ كما أنه من المفترض أن يكون. (كتابة الكونجرس الخاص بك، الخ ...)

واحد مزيد نقطة. بعد إنشاء وسيلة لإستعادة استجابة HTTP الخام بعد استثناء، ما زلت لم تحصل على استجابة كاملة (كما هو محدد من قبل HTTP الشم). كان هذا لأنه عندما أضافت خدمة الويب تطوير رسائل الخطأ HTML إلى بداية الاستجابة، فإنه لم ضبط رأس طول المحتوى، وبالتالي فإن قيمة طول المحتوى كان أقل من حجم الجسم استجابة الفعلي. وكان كل ما حصلت على عدد القيمة طول محتوى الحروف - الباقون في عداد المفقودين. من الواضح، عندما يقرأ صافي تدفق ردا على ذلك، فقط يقرأ عدد طول المحتوى من الشخصيات ولا يسمح لقيمة طول محتوى يجري possibily خطأ. هذا هو ما ينبغي أن يكون؛ ولكن إذا كانت قيمة رأس طول محتوى غير صحيح، والطريقة الوحيدة التي سوف أي وقت مضى الحصول على الجسم استجابة كله مع الشم HTTP (I محلل HTTP المستخدم من <لأ href = "http://www.ieinspector.com" يختلط = "noreferrer"> http://www.ieinspector.com ).

وأنا أفضل أن يكون في إطار القيام تسجيل لك عن طريق ربط في تيار تسجيل الذي يسجل باسم عمليات الإطار الذي وراء تيار. وفيما يلي ليست نظيفة كما كنت ترغب في ذلك، لأنك لا تستطيع أن تقرر بين الطلب والاستجابة في طريقة 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);
        }
    }
}

وأنت لم تحدد ما هي اللغة التي تستخدم ولكن على افتراض C # /. NET هل يمكن استخدام <لأ href = "http://msdn.microsoft.com/en-us/library/esw638yk(VS.71). ASPX "يختلط =" نوفولو noreferrer "> ملحقات SOAP .

وإلا، استخدم الشم مثل يريشارك

وأنا أدرك أنا في وقت متأخر جدا للحزب، ومنذ لم يتم تحديد لغة الواقع، وهنا حل 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

وببساطة استدعاء الدالة وسيعود سلسلة مع أكس تسلسل الكائن كنت تحاول تمريرها إلى خدمة ويب (واقعيا، وهذا يجب أن تعمل لأية وجوه كنت تهتم لرمي في ذلك أيضا).

وكملاحظة جانبية، واستبدال الدعوة في وظيفة قبل أن تعود أكس هي لتجريد من الشخصيات vbCrLf من الإخراج. كان منجم حفنة منهم داخل أكس ولدت ولكن هذا سوف تختلف بالطبع اعتمادا على ما كنت تحاول تسلسل، وأعتقد أنها قد تكون تجريده من خلال الكائن إرسالها إلى خدمة الإنترنت.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top