Visual Basic, .NET, ASP, VBScript
 

   
 
Описание для автора не найдено
 
     
   
 
Аргумент против SOAP кодирования
Автор: Tim Ewald, Корпорация Microsoft (http://www.microsoft.com)
Перевод: Шатохина Надежда(sna@uneta.org), Ukraine .Net Alliance (http://www.uneta.org)
Октябрь 2002
Применяется к:
  • Спецификации Web сервисов (SOAP, WSDL)
  • Обзор:
    Эта статья объясняет, почему SOAP кодирование, также известное как "Section 5 encoding", является тенью от прошлого SOAP, которому нет места в будущем Web сервисов.
    Содерджание:
  • Введение
  • Эволюция SOAP и Web сервисов
  • Суть проблемы
  • Конкретный пример
  • Решение проблемы кодирования
  • Будущее
  • Введение

    SOAP – это краеугольный камень базового протокола Web сервисов. Спецификация SOAP формализует использование XML сообщений в качестве средств связи. Она определяет расширяемую модель, способ представить ошибки протокола и приложений, правила для отправки сообщений через HTTP и рекомендации по преобразованию RPC вызовов в SOAP сообщения. Очень полезно иметь стандартные пути для осуществления этих задач. если их не будет, каждому разработчику, желающему послать XML сообщения через HTTP, придется создавать собственные специальные решения этих проблем, что крайне осложнит процесс взаимодействия. Большинство из предлагаемого SOAP спецификацией полезно, но есть одна не очень хорошая вещь: SOAP кодирование. SOAP кодирование – иногда называемое "Section 5 encoding", по номеру раздела спецификации SOAP 1.1, в котором оно было определено – это тень прошлого SOAP, которому нет места в будущем Web сервисов. Эта статья объясняет почему, начиная с истории.

    Эволюция SOAP и Web сервисов

    Когда была написана первая спецификация SOAP, концепция Web сервисов находилась еще на этапе становления. Люди планировали использовать SOAP в качестве способа лучшего интегрирования технологий распределенных объектов, таких как DCOM, CORBA и RMI, и Internet технологий, таких как XML и HTTP. Целью было создать инфраструктуру, которая производит и потребляет сообщения, основанные на XML, вместо различных двоичных форматов сообщений, предпочитаемых каждой технологией (NDR, CDR и JRMP, соответственно).

    Чтобы клиенты и серверы могли создавать и потреблять сообщения в распределенных приложениях, им надо знать, как эти сообщения должны выглядеть. Чтобы предоставить эту информацию, большинство распределенных объектных систем используют комбинацию скомпилированного прокси/стаб/скелетон (proxy/stub/skeleton) кода и двоичного представления метаданных (как Библиотеки типов COM, Хранилища интерфейсов CORBA или файлы.class Java). SOAP не внес в это никаких изменений. Авторы SOAP спецификации приняли, что разработчик приложения будет гарантировать то, что клиенты и серверы имеют любую информацию, необходимую для правильной обработки SOAP сообщений.

    Однако авторы SOAP понимали, что если они не собираются определить общего способа описания сообщений, они должны, по крайней мере, предоставить некоторые рекомендации для того, как преобразовывать обычные объектно-ориентированные программные конструкции в XML. Для решения этой проблемы они не могли использовать XML Schema (XSD), она была еще далека от завершения. Поэтому они определили модель данных на базе графов нетипизованных структур. Затем они написали правила SOAP кодирования, которые объясняли, как сериализовывать экземпляр модели данных SOAP в SOAP сообщение. Преобразование собственных технологий в модель данных SOAP было оставлено за теми, кто будет реализовывать SOAP.

    Поскольку SOAP получил распространение в промышленности, возникли новые требования. Разработчики хотят загружать описания форматов SOAP сообщений серверов, чтобы можно было создавать клиентов для общения с ними. Поскольку сервер, который хочет предоставлять такого рода описание, не может делать никаких предположений о технологии, используемой для создания клиента, вскрытие описаний сообщений с использованием существующего формата метаданных, такого как Библиотека типов (Type Library), невозможно. Выходом из этой ситуации является использование общего формата метаданных, такого же гибкого, как и сам SOAP, а именно, WSDL. WSDL описывает поведение, поддерживаемое Web сервисом, с помощью portTypes. portType – это коллекция операций. Операции определены в рамках сообщений. Сообщения определены в рамках XML Schema. Сегодня для большинства SOAP и WSDL довольно сложно взаимосвязаны; вместе с UDDI они определяют основные строительные блоки Web сервисов.

    Суть проблемы

    Понимая, что XSD должно сыграть важную роль в описании SOAP сообщений, авторы WSDL выбрали его (все-таки оставив дверь открытой для других вариантов). Понимая, что уже существуют инструментальные средства, реализующие схему SOAP кодирования, авторы спецификации WSDL были вынуждены тоже использовать ее. Решение, к которому они пришли, было определять сообщения в рамках конструкций XML схемы и затем разрешить связывание, чтобы применить к ним SOAP кодирование, если понадобится.

    Связывание определяет конкретные детали, которые вам надо знать, чтобы вызывать операции, определенные portType. (Эта идея не нова. Например, классы COM часто раскрывали свои методы через связывания vtable и IDispatch. Подобным образом, методы класса CORBA обычно доступны через статические заглушки или интерфейс динамического вызова.) Создавая связывание WSDL, которое преобразовывает операции portType в SOAP сообщения, посылаемые через HTTP, вы должны определить, содержат SOAP сообщения литеральные или кодированные экземпляры конструкций схемы, используемых операциями. Если вы выбираете "литеральные", вы говорите, что конструкции XML Schema, которые используют ваши WSDL описания, являются конкретными спецификациями того, что появится в теле вашего SOAP сообщения. Если вы выбираете "кодированные", вы говорите, что конструкции XML Schema, которые используют ваши WSDL описания, являются абстрактными спецификациями того, что появится в теле вашего SOAP сообщения; их можно сделать конкретными, применяя правила, определенные SOAP кодированием. (WSDL спецификация допускает использование и других схем кодирования, но это происходит редко.)

    Это подводит нас к сути проблемы. Как я объяснял ранее, схема SOAP кодирования сериализует модели данных SOAP в XML. Как можно применять схему кодирования для модели данных SOAP к абстрактным описаниям XML Schema, если одна представляет информацию как граф нетипизованных структур, и другие - как дерево типизованных элементов? К сожалению, ни спецификация SOAP (которая определяет SOAP кодирование), ни спецификация WSDL (которая применяет ее для описаний XML схемы) не отвечают на этот вопрос. Кстати, спецификации, описывающей что это значит и как работает, вообще не существует. И это является проблемой.

    Конкретный пример

    До этого момента мои аргументы были лишь теоретическими, поэтому я представлю пример, который поможет сделать их более практическими. Рассмотрим этот псевдо код для операции Distance, которая измеряет расстояние между двумя точками.

    class Point
    {
      public Point() {}
      public Point(int x, int y) { this.x = x; this.y = y; }
      public int x;
      public int y;
    }
    
    float Distance(Point p1, Point p2)
    {
      … // apply the Pythagorean Theorem
    }			
    			

    Вот WSDL документ, который описывает portType Geometry, содержащий операцию Distance. Он также описывает привязку для Geometry portType, который использует SOAP кодирование.

    <wsdl:definitions
     xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
     xmlns:xsd="http://www.w3.org/2001/XMLSchema"
     xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
     xmlns:tns="http://www.gotdotnet.com/team/tewald/sample"
     targetNamespace=
               "http://www.gotdotnet.com/team/tewald/sample">
    
     <wsdl:types>
       <xsd:schema targetNamespace=
               "http://www.gotdotnet.com/team/tewald/sample">
    
         <!-- Point type used by Distance operation -->
    
         <xsd:complexType name="Point">
           <xsd:sequence>
             <xsd:element name="x" type="xsd:int" />
             <xsd:element name="y" type="xsd:int" />
           </xsd:sequence>
         </xsd:complexType>
    
       </xsd:schema>
     </wsdl:types>
    
     <!-- RPC style message definitions -->
    
     <wsdl:message name="DistanceInput">
       <wsdl:part name="p1" type="tns:Point" />
       <wsdl:part name="p2" type="tns:Point" />
     </wsdl:message>
    
     <wsdl:message name="DistanceOutput">
       <wsdl:part name="result" type="xsd:float" />
     </wsdl:message>
    
     <!-- Geometry portType -->
    
     <wsdl:portType name="Geometry">
       <wsdl:operation name="Distance">
         <wsdl:input message="tns:DistanceInput" />
         <wsdl:output message="tns:DistanceOutput" />
       </wsdl:operation>
     </wsdl:portType>
    
     <!-- Binding for Geometry portType that
          uses SOAP encoding -->
    
     <wsdl:binding name="GeometryBinding" type="tns:Geometry">
       <soap:binding style="rpc"
        transport="http://schemas.xmlsoap.org/soap/http" 
       <wsdl:operation name="Distance">
         <soap:operation soapAction="" style="rpc" />
         <wsdl:input message="tns:DistanceInput">
           <soap:body
            namespace="http://www.gotdotnet/team/tewald/sample"
            use="encoded" />
         </wsdl:input>
         <wsdl:output message="tns:DistanceOutput">
           <soap:body
            namespace="http://www.gotdotnet/team/tewald/sample"
            use="encoded" />
         </wsdl:output>
       </wsdl:operation>
     </wsdl:portType>
    
    </wsdl:definitions>			
    			

    Представьте, что вы собираетесь реализовать сервис, который раскрывает этот portType и привязку. Вы хотите, чтобы ваша реализация проверяла, что получаемые ею от клиентов сообщения соответствуют формату, определенному WSDL. Если они не совпадают, вы можете отказаться от них и возвратить ошибку без необходимости что-либо делать. Итак, что составляет корректное сообщение?

    Рассмотрим клиента, который передает два различных экземпляра Point в качестве аргументов в операцию Distance:

    Point one = new Point(10, 20);
    Point two = new Point(100, 200);
    float f = proxy.Distance(one, two);			
    			

    Вот сериализованное сообщение запроса клиента.

    <soap:Envelope xmlns:soap=
             "http://schemas.xmlsoap.org/soap/envelope/">
      <soap:Body soap:encodingStyle=
             "http://schemas.xmlsoap.org/soap/encoding/">
        <ns:Distance xmlns:ns=
             "http://www.gotdotnet.com/team/tewald/samples">
          <p1>
            <x>10</x>
            <y>20</y>
          </p1>
          <p2>
            <x>100</x>
            <y>200</y>
          </p2>
        </ns:Distance>
      </soap:Body>
    </soap:Envelope>			
    			

    На первый взгляд, кажется совершенно ясным, что каждый из экземпляров p1 и p2 (названные по формальным параметрам к ns:Distance) совпадает с описанием схемы для типа Point в WSDL документе. Каждый из них имеет последовательность двух элементов, x и y, значения которых целые (integers). Но этот вывод не состоятелен. Пока модель данных SOAP для описания конкретных значений, таких как x и y, использует простые типы XML Schema (например, xsd:int), она не использует для описания структурированных данных составные типы XML Schema (вот почему я говорил, что модель данных SOAP основывается на нетипизированных структурах). Если модель данных SOAP не использует составные типы, и SOAP кодирование базируется на модели данных SOAP, благоразумно ли будет делать вывод, что p1 и p2 являются SOAP кодированными экземплярами составного типа Point?

    Вы можете подумать, что я влезаю в дебри, как никак, p1 и p2 ужасно похожи на Point. Итак, теперь рассмотрим клиента, который передает тот же экземпляр Point, что и оба аргумента при вызове операции Distance:

    Point one = new Point(10, 20);
    float f = proxy.Distance(one, one);
    			

    Вот сериализованное сообщение запроса клиента. В данном случает клиент использует схему шифрования SOAP для "многоссылочных аксессоров", т.е. один экземпляр Point (one) используется как оба параметра к Distance, p1 и p2.

    <soap:Envelope xmlns:soap=
             "http://schemas.xmlsoap.org/soap/envelope/">
      <soap:Body soap:encodingStyle=
             "http://schemas.xmlsoap.org/soap/encoding/">
        <ns:Distance xmlns:ns=
             "http://www.gotdotnet.com/team/tewald/samples">
          <p1 HREF="#id1"/>
          <p2 HREF="#id1"/>
        </ns:Distance>
        <ns:Point id="id1"
         xmlns:ns="http://www.gotdotnet.com/team/tewald/samples">
          <x>10</x>
          <y>20</y>
        </ns:Point>
      </soap:Body>
    </soap:Envelope>
    			

    В данном случае очевидно, что p1 и p2 не совпадают с описанием схемы для типа Point. Кстати, они даже не подобны. Каждый из них имеет неопределенный атрибут href и ни в одном из них нет обязательных дочерних элементов x и y. Описание входного сообщения операции Distance предлагает, что p1 и p2 являются экземплярами Point, но очевидно, что при наличии кодирования SOAP есть некоторые ситуации, в которых это не так.

    Итак, на чем мы остановились? Иногда элементы p1 и p2 выглядят как сериализованные экземпляры Point, а иногда нет. Теоретически, схема кодирования SOAP должна предупреждать нас о том, как должны выглядеть p1 и p2, за исключением случаев, когда нет определенного способа применить схему кодирования SOAP к типам, определенным с использованием XML Schema, таким как Point. При отсутствии такого определения, реализовать сервер, который раскрывает Geometry portType и привязку и гарантирует, что все получаемые им сообщения корректны, крайне сложно.

    Решение проблемы кодирования

    Здесь вы, возможно, думаете, что это в действительности не ваша проблема, потому что вы не реализовываете ваши Web сервисы с нуля. Вы используете инструментальные средства Web сервиса и считаете, что они должны учитывать эти детали вместо вас. Но это не решает проблемы; это просто передвигает ее от вас к разработчикам инструментальных средств, которым придется решить, что делать. До сих пор они обеспечивали поддержку для SOAP кодирования (несмотря на проблемы, описанные мною в предыдущем разделе), но цена слишком высока. Большинство инструментальных средств используют различные пути для литеральных и кодированных привязок; по существу, реализовывая два уровня маршаллинга, по одному на каждый случай. Среди разработчиков, которые потратили время, работая над этой проблемой, бытует мнение, что нам нужно найти лучшее решение. Тем или иным образом мы должны примирить SOAP кодирование и XML Schema.

    К счастью, существует совершенно прямое решение. Правила SOAP кодирования определяют превращение модели данных SOAP в XML сообщения. Нет смысла применять SOAP кодирование для описаний в XML Schema, потому что между ними нет установленных взаимоотношений. Но что будет, если вы напишете схему, которая описывает XML сообщения, созданные SOAP кодированием?

    Вот переработанное описание WSDL для Geometry portType, который использует этот подход.

    <wsdl:definitions
     xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
     xmlns:xsd="http://www.w3.org/2001/XMLSchema"
     xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
     xmlns:tns="http://www.gotdotnet.com/team/tewald/sample"
     targetNamespace=
               "http://www.gotdotnet.com/team/tewald/sample">
    
     <wsdl:types>
       <xsd:schema targetNamespace=
               "http://www.gotdotnet.com/team/tewald/sample">
    
         <!-- Point type used by Distance operation,
              note use optional attributes and
              element content -->
    
         <xsd:complexType name="Point">
           <xsd:sequence minOccurs="0">
             <xsd:element name="x" type="xsd:int" />
             <xsd:element name="y" type="xsd:int" />
           </xsd:sequence>
           <xsd:attribute name="id"
                type="xsd:ID" use="optional" />
           <xsd:attribute name="href"
                type="xsd:anyURI" use="optional" />
         </xsd:complexType>
    
       </xsd:schema>
     </wsdl:types>
    
     <!-- RPC style message definitions -->
    
     <wsdl:message name="DistanceInput">
       <wsdl:part name="p1" type="tns:Point" />
       <wsdl:part name="p2" type="tns:Point" />
     </wsdl:message>
    
     <wsdl:message name="DistanceOutput">
       <wsdl:part name="result" type="xsd:float" />
     </wsdl:message>
    
     <!-- Geometry portType -->
    
     <wsdl:portType name="Geometry">
       <wsdl:operation name="Distance">
         <wsdl:input message="tns:DistanceInput" />
         <wsdl:output message="tns:DistanceOutput" />
       </wsdl:operation>
     </wsdl:portType>
    
     <!-- Binding for Geometry portType that
          uses SOAP encoding -->
    
     <wsdl:binding name="GeometryBinding" type="tns:Geometry">
       <soap:binding style="rpc"
        transport="http://schemas.xmlsoap.org/soap/http" 
       <wsdl:operation name="Distance">
         <soap:operation soapAction="" style="rpc" />
         <wsdl:input message="tns:DistanceInput">
           <soap:body
            namespace="http://www.gotdotnet/team/tewald/sample"
            use="literal" />
         </wsdl:input>
         <wsdl:output message="tns:DistanceOutput">
           <soap:body
            namespace="http://www.gotdotnet/team/tewald/sample"
            use="literal" />
         </wsdl:output>
       </wsdl:operation>
     </wsdl:portType>
    
    </wsdl:definitions>			
    			

    Обновленное описание типа Point включает в качестве содержимого элемента типа описания двух новых атрибутов, id и href, которые являются необязательными. Эти изменения в Point обеспечивают возможность существования экземпляров, эквивалентных тем, которые создавались SOAP кодированием при сериализации многоссылочных данных. Экземпляр Point может включать атрибут id и потомков элементов x и y или атрибут href без потомков. Первое представляет экземпляр Point; второе – ссылку на Point. Поскольку описание XML Schema уже создало эквивалент SOAP кодирования, нет необходимости применять к нему правила кодирования, таким образом, привязка для Geometry portType обновлена для использования схемы литеральных типов (атрибут encodingStyle был удален), следовательно, устранена любая неоднозначности по поводу того, являются ли параметры операции Distance, p1 and p2, экземплярами Point.

    Использует ли этот подход SOAP кодирование? Это зависит от вашей перспективы. Выполняется, по существу, то же, что делает и SOAP кодирование без попытки применить правила SOAP кодирования непосредственно к схеме, чем плохо. Ключевое преимущество в том, что описание XML Schema, включенное в WSDL, точно описывает сообщения, необходимые portType и привязке. Это значительно упрощает реализацию сервиса, который перед обработкой сообщений проверяет их корректность.

    Мы должны предпринять несколько шагов, чтобы получить широкую поддержку этого подхода. Во-первых, нам надо определить стандартные, глобальные атрибуты для представления ссылок на узлы в сериализованном графе. В моем примере я определил локальные атрибуты id и href как часть типа Point. Проблемой этого подхода является то, что каждый тип, который может использоваться в сериализованном графе, должен определить эквивалентные атрибуты с именно такой же семантикой. В противном случае, инструментальным средствам придется принять, что любой тип, имеющий атрибуты id и href, намеревается использовать их для описания графа. Если мы создаем глобальные атрибуты и определяем типы, которые их используют, нам не надо определять атрибуты для каждого типа, и инструментальным средствам надо будет только распознать одну пару атрибутов. (В The WS-I Basic Profile Working Group (http://www.ws-i.org/docs/charters/WSBasic_Profile_Charter.pdf) эта работа уже проделана при попытке помочь разрешить проблемы взаимодействия Web сервисов, хотя эта работа еще не опубликована к моменту написания этой статьи.)

    Если стандартные глобальные атрибуты существуют, создатели инструментальных средств могут позаимствовать их. Им придется внести, по существу, два изменения. Во-первых, необходимо будет обновить их инструментарий WSDL, чтобы он мог производить и потреблять XML Schemas, которые включают ссылочные атрибуты для любого типа, который может использоваться для представления узла сериализованного графа. Затем придется обновить инфраструктуру среды выполнения, чтобы сериализовать и десериализовать дерево в XML сообщения, которые используют эти атрибуты.

    Будущее

    Многие люди, включая и меня, верят, что уход от SOAP кодирования неизбежен. Текущий проект спецификации SOAP 1.2 рабочей группы W3C XML Protocol делает поддержку SOAP кодирования необязательной (т.е. инструментальные средства могут соответствовать SOAP 1.2 без поддержки SOAP кодирования), текущий проект рекомендаций по взаимодействию рабочей группы WS-I Basic Profile запрещает использование SOAP кодирования с SOAP 1.1, и рабочая группа W3C Web Service Description решила выбросить поддержку кодирования из их последнего рабочего проекта спецификации WSDL 1.2.

    Отражение этих изменений в инструментальных средствах займет некоторое время, сначала мы должны обратиться к благоприятному для схемы способу сериализации графов. Затем необходимо обновить инструментальные средства. На это понадобится некоторое время, но ожидание того стоит. И наконец, будет намного проще реализовывать и использовать стек Web сервисов.

    Никакая часть настоящей статьи не может быть воспроизведена или передана в какой бы то ни было форме и какими бы то ни было средствами, будь то электронные или механические, если на то нет письменного разрешения владельцев авторских прав.

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

       
       
         
      VBNet рекомендует