На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: maxim84_
  
> WCF , Использование IClientMessageInspector для форматирования SOAP сообщени
    Сегодня я хочу поделиться с Вами, толикой опыта, который был получен мной при разработке распределенных систем ориентированных на сервисы. Все, наверное, слышали, от маркетологов, что с внедрением Enterprise систем ориентированных на работу со службами, наступит «коммунизм». Вавилон 21 века падет. Программы, написанные на различных языках и предназначенные для работ на различных платформах смогут общаться друг с другом посредством сообщений и будут легко маштабироваться. Маркетологи , вероятно не врут, но на мой взгляд они как всегда не договаривают нам всю правду. Отсутствие корректных реализаций стандартов, привели к тому, что на рынке присутствует огромное число платформ для разработки веб-служб не совместимых друг с другом! Не верите? Не так давно, мне позвонил один из моих зарубежных коллег, который работал над одним из крупных интеграционных проектов. Проект являл собой клиент для существующих веб-служб предпиятия, реализованных на платформе Java одним из известных «java-вендоров». На первой стадии проекта, заказчиком были предоставлены wsdl контракты, после чего команда в течении нескольких месяцев разрабатывала интеграционное решение, между приложением генерации рекламного контента и внутренними веб-службами заказчика. Они не знали, что их ждет. Оказалось, что .NET клиент, не может корректно интегрироваться в существующую систему. На последней стадии проекта планово стартовала интеграция, каким же было удивление моего коллеги, когда оказалось, что клиенты не могут взаимодействовать с java службами, по причине … ограниченной soap десериализации на стороне java служб.
    Суть происходящего можно продемонстрировать следующими SOAP сообщениями.
    Такое сообщение обычно формируется .NET клиентом:

    ExpandedWrap disabled
      <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://schemas.xxx.com/2008/10/dealer">
         <soapenv:Header/>
         <soapenv:Body>
            <getInventory xmlns="http://schemas.xxx.com/2008/10/dealer">
               <getInventoryRequest>
                  <dealerId>51796410</deal:dealerId>
               </getInventoryRequest>
            </getInventory>
         </soapenv:Body>
      </soapenv:Envelope>


    Вот, что примерно ожидало java приложение:

    ExpandedWrap disabled
      <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:deal="http://schemas.xxx.com/2008/10/dealer">
         <soapenv:Header/>
         <soapenv:Body>
            <deal:getInventory>
               <deal:getInventoryRequest>
                  <deal:dealerId>51796410</deal:dealerId>
               </deal:getInventoryRequest>
            </deal:getInventory>
         </soapenv:Body>
      </soapenv:Envelope>


    Не сложно убедиться, что два сообщения с точки зрения XML эквивалентны, но .NET клиент передает данные с полностью квалифицированными пространствами имен, а Java сервер, по не понятной лично мне причине отказывается принимать такое SOAP сообщения требуя явного использования префикса deal который замаплен в корневом элементе представляющим собой конверт!
    Как не странно, но легкого пути заставить сериализовать SOAP сообщения именно так, в .NET не имеется. Можно пытаться играться с XmlSerializerNamespaces, при сериализации .NET типов, но решение не выглядит универсальным. Есть ли удобаваримое решение для этого? Мне кажется, что да. Тут многое зависит от того, что именно используется на .NET стороне для взаимодействия с веб-службами. Прокси полученный на основе web reference или используется модная нынче WCF, настроенная на использование basicHttpBinding для оконечной точки. В нашем случае использовался WCF прокси. Решение которое было предложено базировалось на реализации интерфейса IClientMessageInspector. Не сложно предположить, что реализовав интерфейсс с таким именем мы получим возможность перехватывать SOAP сообщения, но самое главное мы получим возможность модифицировать их. Интерфейсс определяет пару методов, один из которых позволяет получить сообщение непосредственно перед отправкой клиенту, а второй позволяет просматривать полученное от клиента сообщение. Ниже преведена реализация такого интерфейса, решающая проблему неймспейсов в лоб.

    ExpandedWrap disabled
          public class SoapInspecotor : IClientMessageInspector
          {
              public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
              {
       
              }
       
              public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel)
              {
                  string envelopPrefix = "envelop";
                  string dealPrefix = "deal";
                  XNamespace one = "http://schemas.xxx.com/2008/10/dealer";
                  XNamespace second = "http://schemas.xmlsoap.org/soap/envelope/";
       
                  XDocument document = XDocument.Parse(request.ToString());
       
                  foreach (var elm in document.Descendants(second + "Body").Elements())
                  {
                      elm.ReplaceWith(new XElement(elm.Name, new XAttribute(XNamespace.Xmlns + "deal", one), elm.Elements()));
       
                  }
       
                  document.Root.ReplaceAttributes(new XAttribute(XNamespace.Xmlns + dealPrefix, one), new XAttribute(XNamespace.Xmlns + envelopPrefix, second));
       
                  Message replacedMessage = Message.CreateMessage(document.CreateReader(), int.MaxValue, MessageVersion.Soap11);
                  request = replacedMessage;
       
                  return null;
              }
          }


    Результат на выходе мы получаем «совместимое» SOAP сообщение:

    ExpandedWrap disabled
      <envelop:Envelope xmlns:deal="http://schemas.xxx.com/2008/10/dealer" xmlns:envelop="http://schemas.xmlsoap.org/soap/envelope/">
        <envelop:Header>
          <Action envelop:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://schemas.xxx.com/2008/10/dealer/getInventoryRequest</Action>
        </envelop:Header>
        <envelop:Body>
          <deal:getInventoryRequest xmlns:deal="http://schemas.xxx.com/2008/10/dealer">
            <deal:dealerId>51796410</deal:dealerId>
          </deal:getInventoryRequest>
        </envelop:Body>
      </envelop:Envelope>


    Задача была бы на порядок менее приятной если бы не Link to Xml. Это первый плюс, второй плюс это WCF которая в очередной раз демонстрирует свою гибкость и прозрачность для конечного разработчика.
    Что бы интегрировать перехватчик в приложение понадобиться кастомного имплементация поведения:

    ExpandedWrap disabled
          public class InterceptorCustomBehavior : IEndpointBehavior
          {
              public void AddBindingParameters(ServiceEndpoint serviceEndpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
              { }
       
              public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, System.ServiceModel.Dispatcher.ClientRuntime behavior)
              {
                  behavior.MessageInspectors.Add(new SoapInspecotor());
              }
       
              public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
              { }
       
              public void Validate(ServiceEndpoint serviceEndpoint)
              { }
          }


    Метод ApplyClientBehavior добавляет наш кастомный перехватчик в инфраструктуру WCF. После этого очень просто добавить наше поведение к прокси классу.

    ExpandedWrap disabled
                  Service1SoapClient client = new Service1SoapClient();
                  client.Endpoint.Behaviors.Add(new InterceptorCustomBehavior());


    Вот собственно и все теперь все запросы будут попадать в перехватчик. Альтернативно можно не писать класс реализующий поведение, а можно зарегистрировать перехватчик в конфигурационном файле.
    Для чего еще может быть использован интерфейс IClientMessageInspector? Да собственно для чего угодно, для логирования или для встраивания дополнительной форматирующей логики, для модификации всего сообщения или только его тела для сжатия сообщения и много, много другого.
    Что же делать тем кто работает со старой реализацией прокси классов? Думаю, что можно реализовать аналогичное решение базирующиеся на написании soap extention для web-службы.

    До новых встреч.
    0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
    0 пользователей:


    Рейтинг@Mail.ru
    [ Script execution time: 0,0196 ]   [ 15 queries used ]   [ Generated: 25.04.24, 06:21 GMT ]