7 сентября 2019 г.

Как получить SOAP-XML сообщения прямого и callback вызова.

Ниже приведён код, на примере которого будет проводиться демонстрация. 
Примечания к коду:
  1. Для прямого вызова будем выводить SOAP сообщение метода SayHello() контракта IMyService.
  2. Для Callback вызова будем выводить SOAP сообщение метода Wellcome() контракта IMyCallback.
  3. Используется привязка WSDualHttpBinding работающая с HTTP транспортом и передающая SOAP сообщения в текстовом виде с настройкой Security=None. Если у вас net.tcp привязка, тогда вам подойдёт только способ 1 и 2.

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
 
namespace ConsoleApp 
{
    class Program
    {
        /// <summary>
        /// Client callback instance.
        /// </summary>
        class MyCallback : IMyCallback
        {
            public void Wellcome(string name)
            {
                Console.WriteLine(name);
            }
        }
 
        private const string MyAddress = "http://localhost:9202";
 
        private static readonly Binding MyBinding = new WSDualHttpBinding()
        {
            Security = { Mode = WSDualHttpSecurityMode.None}
        };
 
        static void Main(string[] args)
        {
            // Start service
            StartService();
 
            // Get service client
            var serviceClient = GetServiceClient();
 
            // Use service
            serviceClient.SayHello("Sergey Vaulin", 31);
 
            Console.ReadKey();
        }
 
        private static IMyService GetServiceClient()
        {
            return DuplexChannelFactory<IMyService>.CreateChannel(
                new InstanceContext(new MyCallback()),
                MyBinding, 
                new EndpointAddress(MyAddress));
        }
 
        private static void StartService()
        {
            var host = new ServiceHost(typeof(MyService));
            host.AddServiceEndpoint(typeof(IMyService), MyBinding, MyAddress);
            host.Open();
        }
    }
 
    /// <summary>
    /// 1. My service contract.
    /// </summary>
    [ServiceContract(CallbackContract = typeof(IMyCallback))]
    interface IMyService
    {
        [OperationContract]
        void SayHello(string name, int age);
    }
 
    /// <summary>
    /// 2. My service callback contract.
    /// </summary>
    [ServiceContract]
    interface IMyCallback
    {
        [OperationContract]
        void Wellcome(string name);
    }
 
    /// <summary>
    /// 3. My service type.
    /// </summary>
    [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
    class MyService : IMyService
    {
        public void SayHello(string name, int age)
        {
            var message = OperationContext.Current.RequestContext.RequestMessage.ToString();
 
            var callback = OperationContext.Current.GetCallbackChannel<IMyCallback>();
            callback.Wellcome(string.Format("You are welcome {0} ({1})", name, age));
        }
    }
}

Способ 1. Использование <system.diagnostics> логирование.


Для данного способа достаточно в файле конфигурации добавить секцию настроек.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.diagnostics>
    <sources>
      <source name="System.ServiceModel.MessageLogging">
        <listeners>
          <add name="messages"
               type="System.Diagnostics.XmlWriterTraceListener"
               initializeData="WCFLog.svclog" />
        </listeners>
      </source>
    </sources>
  </system.diagnostics>
  <system.serviceModel>
    <diagnostics>
      <!-- https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/wcf/messagelogging -->
      <messageLogging
        logEntireMessage="true"
        logMalformedMessages="true"
        logMessagesAtServiceLevel="true"
        logMessagesAtTransportLevel="false"
        maxMessagesToLog="100"
        maxSizeOfMessageToLog="5000"/>
    </diagnostics>
  </system.serviceModel>
</configuration>

После чего можно запустить приложение и рядом с исполняемым файлом появится файл WCFLog.svclog, который можно отрыть утилитой SvcTraceViewer.exe, устанавливаемая с Windows SDK.

Открыв файл программой в списке справа у вас будут отображаться все вызовы сервиса, а так же вкладка Messages у каждого вызова, в которой в теге <Envelope> содержится тело сообщения.

Внимание! Что вне тега Envelope не является SOAP сообщением.


Комментарии к разбору сообщения:

<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
  <s:Header>
    <a:Action s:mustUnderstand="1">http://tempuri.org/IMyService/SayHello</a:Action>
    <a:MessageID>urn:uuid:e3ff2900-02b5-49dd-90bd-1fb76e37d74d</a:MessageID>
    <a:ReplyTo>
      <a:Address>http://sap/Temporary_Listen_Addresses/d3703051-d88f-4ffe-b3f3-f0493585664d/2cc9adc0-8283-4c9a-827d-157df8aae4e8</a:Address>
    </a:ReplyTo>
  </s:Header>
  <s:Body>
    <!-- Вызов метода SayHello -->
    <SayHello xmlns="http://tempuri.org/">
      <!-- Значение параметра name -->
      <name>Sergey Vaulin</name>
 
      <!-- Значение параметра age -->
      <age>31</age>
    </SayHello>
  </s:Body>
</s:Envelope>

Все методы в моём примере работают в режиме Request\Responce, а значит после вызова я получаю ответ в виде <SayHelloResponce />

<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
  <s:Header>
    <a:Action s:mustUnderstand="1">http://tempuri.org/IMyService/SayHelloResponse</a:Action>
  </s:Header>
  <s:Body>
    <SayHelloResponse xmlns="http://tempuri.org/"></SayHelloResponse>
  </s:Body>
</s:Envelope>

Данное сообщение получено не будет если у метода контракта стоит значение IsOneWay=true.

Способ 2. Получение сообщения используя RequestContext.


В теле метода SayHello(), на серверной стороне, необходимо обратиться к свойству:

var message = OperationContext.Current.RequestContext.RequestMessage.ToString();

В котором лежит принятое сообщение:


Способ 3. Использование Traffic Sniffer.


Является один из самых простых способов, но требует умение пользоваться хотя бы одним из снифферов. Лично я использую два самых популярных для Windows:

  1. WireShark - очень навороченный сниффер, который мониторит весь входящий и исходящий трафик.
  2. Fiddler - удобный HTTP сниффер, но требующий настройки.

Какой именно Sniffer использовать решать вам, но надо иметь ввиду следущее:

Не все sniffer умеют работать с loopback. Когда общение клиента и сервера происходит на одной машине. В этом случае система игнорирует канальный уровень и маршрутизирует запросы минуя сетевой уровень.

Примеры программ выше умею это делать, продемонстрирую все на примере WireShark версия 3.0.3 в котором есть Npcap Loopback Adapter.

Запускаем и выбираем Loopback адаптер:


Поскольку в моём примере используется порт 9202 я добавлю фильтр по нему, что бы не видеть мусорный трафик.

Фильтр:
http && (tcp.port == 9202 || tcp.dstport == 9202)

Запускаем и видим наш запрос:


После чего можно сделать Follow - TCP Stream.


И увидеть все запросы в хронологическом порядке:


Комментариев нет:

Отправить комментарий