programing

ASP.net에서 실행되고 있는 Web Reference Client에서 RAW Soap 데이터 가져오기

sourcetip 2023. 4. 22. 23:00
반응형

ASP.net에서 실행되고 있는 Web Reference Client에서 RAW Soap 데이터 가져오기

현재 진행 중인 프로젝트에서 웹 서비스 클라이언트를 촬영하려고 합니다.서비스 서버(LAMP)의 플랫폼을 알 수 없습니다.저는 고객과의 잠재적인 문제를 제거했기 때문에 그들 쪽에 결함이 있다고 생각합니다.클라이언트는 표준 ASMX 타입의 Web 참조 프록시 auto로 서비스 WSDL에서 생성됩니다.

필요한 것은 RAW SOAP 메시지(요구 및 응답)입니다.

어떻게 하면 좋을까요?

은 다음과 .web.configSOAP(Request/Response)를 선택합니다. 미가공 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을 구현하여 로그 파일에 대한 전체 요청 및 응답을 기록할 수 있습니다.그런 다음 web.config에서 SoapExtension을 활성화하면 디버깅을 위해 쉽게 켜거나 끌 수 있습니다.여기 제가 직접 사용하기 위해 찾아서 수정한 예가 있습니다.이 예에서는 로그는 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 또는 serializer 클래스가 왜 이렇게 야단법석인지 모르겠다.아래 코드가 유효했습니다.

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

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

Fiddler2를 사용해 보세요.요구와 응답을 검사할 수 있습니다.Fiddler는 http 트래픽과 https 트래픽 모두에서 동작합니다.

웹 참조 호출에서 예외가 발생하면 팀 카터의 솔루션이 작동하지 않는 것 같습니다.예외가 발생하면 에러 핸들러에서 (코드로) 확인할 수 있도록 raw web resonse에 접근하려고 했습니다.그러나 콜에서 예외가 발생하면 팀의 메서드에 의해 작성된 응답 로그가 공백인 것을 알 수 있습니다.코드를 완전히 이해하지는 못하지만, Tim의 방법은 다음 단계부터 프로세스를 절단하는 것으로 보입니다.Net은 이미 웹 응답을 무효화하고 폐기했습니다.

저는 로우 레벨 코딩으로 수동으로 웹 서비스를 개발하는 고객과 함께 일하고 있습니다.이 시점에서 SOAP 형식의 응답 전에 HTML 형식의 메시지로 자체 내부 프로세스 오류 메시지를 응답에 추가합니다.물론, 오토매직이죠.인터넷 웹 참조가 폭발합니다.예외가 느려진 후에 raw HTTP 응답을 얻을 수 있으면 혼합된 반환 HTTP 응답 내에서 SOAP 응답을 검색 및 해석할 수 있으며 데이터가 정상적으로 수신되었는지 여부를 알 수 있습니다.

나중에...

실행 후에도 기능하는 솔루션을 다음에 나타냅니다(응답 후에만 요청을 받을 수 있습니다).

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

Soap Message를 검색하면.스트림, 이 시점에서 데이터를 검사하는 데 사용할 수 있는 읽기 전용 스트림이어야 합니다.스트림을 읽으면 데이터가 없는 후속 처리 폭탄이 발견되어 위치를 처음부터 리셋할 수 없기 때문입니다.

흥미로운 것은 ChainStream과 ProcessMessage의 두 가지 방식 모두 실행했을 경우 ChainStream에서 스트림유형을 ConnectStream에서 MemoryStream으로 변경했기 때문에 ProcessMessage 메서드가 기능하고 MemoryStream에서는 검색 조작이 허용되기 때문입니다.(ConnectStream을 MemoryStream에 캐스팅하려고 했지만 허용되지 않았습니다.)

그래서...Microsoft는 ChainStream 유형에 대한 검색 작업을 허용하거나 SoapMessage를 만들어야 합니다.읽기 전용 카피를 그대로 스트리밍합니다.(의원 등)

한 가지 더요.예외 후에 raw HTTP 응답을 취득하는 방법을 작성했는데, 아직 완전한 응답을 얻지 못했습니다(HTTP 스니퍼에 의해서 판단).이는 개발 웹 서비스가 HTML 오류 메시지를 응답 처음에 추가했을 때 Content-Length 헤더를 조정하지 않았기 때문에 Content-Length 값이 실제 응답 본문의 크기보다 작았기 때문입니다.내가 얻은 것은 Content-Length 값 숫자뿐이었고 나머지는 누락되었습니다.당연히 언제죠?Net은 응답 스트림을 읽고 Content-Length 문자 수를 읽을 뿐 Content-Length 값이 잘못될 가능성은 없습니다.이것은 당연하지만 Content-Length 헤더 값이 잘못된 경우 응답 본문 전체를 취득하는 유일한 방법은 HTTP 순이퍼(http://www.ieinspector.com의 사용자 HTTP Analyzer)입니다.

다음은 상위 답변의 간략한 버전입니다.이 항목을 에 추가합니다.<configuration>의 요소web.config또는App.config파일을 만듭니다.trace.log프로젝트 파일 작성bin/Debug폴더입니다.또는 를 사용하여 로그 파일의 절대 경로를 지정할 수 있습니다.initializeData기여하다.

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

경고합니다.maxdatasize그리고.tracemodeAtribut은 허용되지 않지만 로깅할 수 있는 데이터 양이 증가하고 16진수로 모든 것이 로깅되지 않도록 합니다.

기본 스트림을 처리하는 프레임워크로 로그되는 로깅 스트림을 후크하여 사용자를 위해 프레임워크가 로깅을 수행하기를 원합니다.Chain Stream 방식으로는 요청과 응답 중 어느 쪽을 선택할 수 없기 때문에 아래는 제가 원하는 만큼 깨끗하지 않습니다.다음은 제가 대처하는 방법입니다.존 해나에게 감사드리며 스트림 아이디어를 무시해주고

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 SOAP 확장을 사용할 수 있습니다.

그 이외의 경우는 Wireshark 등의 순이퍼를 사용합니다.

다음 명령어는 raw 메시지를 c#으로 가져옵니다.

OperationContext.Current.RequestContext.RequestMessage

비누연장제를 쓰느라 3일이나 허비했는데 생각보다 너무 쉬웠어요.SOAP 익스텐션은 권장되지 않습니다.MSDN 클라이언트에서의 메시지 검사 또는 변경 방법메시지클래스의 사용을 권장합니다.

파티에 늦었다는 걸 깨달았어요. 그리고 실제로 언어가 명시되지 않았기 때문에 여기 VB가 있습니다.Bimmerbound의 답변을 기반으로 한 NET 솔루션. 누군가 우연히 이 문제를 발견하고 솔루션이 필요할 경우에 대비합니다.참고: 프로젝트의 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에는 여러 개의 파일이 포함되어 있지만, 시리얼화하려는 항목에 따라 분명히 달라지며 웹 서비스로 전송되는 개체 중에 삭제될 수 있습니다.

언급URL : https://stackoverflow.com/questions/300674/getting-raw-soap-data-from-a-web-reference-client-running-in-asp-net

반응형