Как записать поток HTTP-уровня с клиента WCF SOAP на .NET Core? - PullRequest
0 голосов
/ 16 ноября 2018

Справочная информация: я поддерживаю платформу интеграции, которая извлекает данные из различных ненадежных API.Некоторые из этих действий приводят к потенциально высоким затратам, поэтому в целях диагностики каждое исходящее и входящее сообщение записывается на диск в отдельный файл.Для REST-подобных API я использую простую оболочку сетевого потока, которая также сохраняет данные в файл.Для .NET Classic SOAP-клиента у меня есть помощник, который динамически оборачивает SoapHttpClientProtocol для использования того же механизма ведения журнала сетевого потока.

С .NET Standard 2.0 и .NET Core , единственный поддерживаемый способ написания SOAP-клиентов - это WCF.Как мне программно настроить клиент WCF SOAP для регистрации входящих / исходящих потоков HTTP в отдельных файлах, предпочтительно с настраиваемыми именами?

Мой текущий пример кода клиента:

public abstract class ServiceCommunicatorBase<T>
    where T : IClientChannel
{
    private const int Timeout = 20000;

    private static readonly ChannelFactory<T> ChannelFactory = new ChannelFactory<T>(
        new BasicHttpBinding(),
        new EndpointAddress(new Uri("http://target/endpoint")));

    protected T1 ExecuteWithTimeoutBudget<T1>(
        Func<T, Task<T1>> serviceCall,
        [CallerMemberName] string callerName = "")
    {
        // TODO: fixme, setup logging
        Console.WriteLine(callerName);

        using (var service = this.CreateService(Timeout))
        {
            // this uses 2 threads and is less than ideal, but legacy app can't handle async yet
            return Task.Run(() => serviceCall(service)).GetAwaiter().GetResult();
        }
    }

    private T CreateService(int timeout)
    {
        var clientChannel = ChannelFactory.CreateChannel();
        clientChannel.OperationTimeout = TimeSpan.FromMilliseconds(timeout);
        return clientChannel;
    }
}

public class ConcreteCommunicator
    : ServiceCommunicatorBase<IWCFRemoteInterface>
{
    public Response SomeRemoteAction(Request request)
    {
        return this.ExecuteWithTimeoutBudget(
            s => s.SomeRemoteAction(request));
    }
}

1 Ответ

0 голосов
/ 21 ноября 2018

Мне удалось зарегистрировать сообщения, используя IClientMessageInspector, прикрепленное через настраиваемое поведение. Есть некоторая документация для инспекторов сообщений на MSDN , но она все еще неясна (и несколько устарела, netstandard-2.0 Поверхность API немного отличается.

Мне пришлось перейти от использования ChannelFactory<T> к использованию фактического сгенерированного прокси-класса. Рабочий код (сокращенно для краткости):

var clientChannel = new GeneratedProxyClient(
  new BasicHttpBinding { SendTimeout = TimeSpan.FromMilliseconds(timeout) },
  new EndpointAddress(new Uri("http://actual-service-address"));

clientChannel.Endpoint.EndpointBehaviors.Add(new LoggingBehaviour());

Классы обслуживания:

// needed to bind the inspector to the client channel
// other methods are empty
internal class LoggingBehaviour : IEndpointBehavior
{
    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        clientRuntime.ClientMessageInspectors.Add(new LoggingClientMessageInspector());
    }
}

internal class LoggingClientMessageInspector : IClientMessageInspector
{
    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
        var correlationId = Guid.NewGuid();
        this.SaveLog(ref request, correlationId, "RQ");

        return correlationId;
    }

    public void AfterReceiveReply(ref Message reply, object correlationState)
    {
        var correlationId = (Guid)correlationState;
        this.SaveLog(ref reply, correlationId, "RS");
    }

    private void SaveLog(ref Message request, Guid correlationId, string suffix)
    {
        var outputPath = GetSavePath(suffix, correlationId, someOtherData);
        using (var buffer = request.CreateBufferedCopy(int.MaxValue))
        {
            var directoryName = Path.GetDirectoryName(outputPath);
            if (directoryName != null)
            {
                Directory.CreateDirectory(directoryName);
            }

            using (var stream = File.OpenWrite(outputPath))
            {
                using (var message = buffer.CreateMessage())
                {
                    using (var writer = XmlWriter.Create(stream))
                    {
                        message.WriteMessage(writer);
                    }
                }
            }

            request = buffer.CreateMessage();
        }
    }
}
...