Понимание SOAP
Viktor Shatokhin , Aaron Skonnard, DevelopMentor
Изначально SOAP расшифровывался как "Протокол доступа простых объектов" (Simple Object Access Protocol). Если бы вы спросили кого-нибудь о том, что значит SOAP, несколько лет назад, то получили бы примерно такой ответ:"это для работы DCOM и Corba (т.е. RPC вызовов) через Internet". Первые авторы признавали тогда, что они фокусировались на "доступе к объектам", но со временем захотелось, чтобы SOAP обслуживал более широкую аудиторию. Таким образом, фокус спецификации быстро сместился с объектов на обобщенную оболочку обмена XML сообщениями.
Этот переход создал небольшую проблему с "O" в аббревиатуре SOAP. Интересно, что Рабочая группа SOAP 1.2 сохранила (до сих пор) имя SOAP (оно так популярно, как же может быть иначе?), но было решено не расшифровывать его, чтобы не вводить разработчиков в заблуждения. Сегодня в официальном определении, приводимом в самых последних спецификациях SOAP 1.2, объекты даже не упоминаются:
SOAP – это облегченный протокол, предназначенный для обмена структурированной информацией в децентрализованной, распределенной среде. Для определения наращиваемой оболочки обмена сообщениями, обеспечивающей структуру сообщения, которая может быть использована при обмене различными базовыми протоколами, SOAP использует XML технологии. Эта оболочка разработана независимой от любой конкретной модели программирования и других особых семантик реализации.
Это определение действительно отражает суть того, чем является SOAP сегодня. SOAP определяет способ перемещения XML сообщений из точки А в точку В (см. Рисунок 1). Это делается путем обеспечения оболочки обмена сообщениями на базе XML, которая является: 1) наращиваемой; 2) пригодной к использованию различными базовыми сетевыми протоколами; 3) независимой от моделей программирования. Давайте немного более детально остановимся на каждой из этих трех характеристик.
Рисунок 1. Простой обмен сообщениями SOAP
Во-первых, ключевым в SOAP является его наращиваемость. Когда аббревиатура еще что-то означала, буква "S" имела значение "Простой". Если мы хоть чему-то и научились в Web, так это тому, что простота всегда одерживает верх над эффективностью или техническим качеством, и когда делается ставка на способность к взаимодействию, она становится абсолютным требованием. Простота остается одной из основных целей при разработке SOAP, что доказывает отсутствие в SOAP различных возможностей распределенных систем, таких как безопасность, маршрутизация, надежность и т.д. SOAP определяет оболочку взаимосвязи, в которой есть возможность добавить эти возможности в будущем как многоуровневые расширения. Microsoft, IBM и другие производители программного обеспечения активно работают над созданием общего пакета расширений SOAP, которые добавят многие из этих возможностей, ожидаемых большинством разработчиков. Первым шагом стала Глобальная архитектура XML Web сервисов (Global XML Web Services Architecture (GXA)). Microsoft уже выпустила реализацию некоторых спецификаций GXA под названием Расширения Web сервисов 1.0 SP1 для Microsoft .NET (Web Services Enhancements 1.0 SP1 for Microsoft .NET (WSE)).
Во-вторых, SOAP может использоваться в любом транспортном протоколе, таком как TCP, HTTP, SMTP или даже MSMQ (см. Рисунок 1). Тем не менее, чтобы поддерживать возможность взаимодействия, должны быть определены взаимосвязи co стандартными протоколами, структура которых различна для каждой среды. Спецификация SOAP обеспечивает гибкую оболочку для определения взаимосвязей произвольных протоколов и сегодня обеспечивает явное связывание для HTTP, поскольку он так широко используется.
В-третьих, SOAP разрешен для всех моделей программирования и не привязан к RPC. Большинство разработчиков сразу же отождествили SOAP с осуществлением RPC вызовов к распределенным объектам (т.к. первоначально говорилось об "организации доступа к объектам"), в то время как в действительности фундаментальная модель SOAP более близка к традиционным системам обмена сообщениями, таким как MSMQ. SOAP определяет модель обработки отдельных, однонаправленных сообщений. Вы можете объединить множество сообщений в общий процесс обмена сообщениями. Рисунок 1 иллюстрирует простое однонаправленное сообщение, при котором отправитель не получает ответа. Однако получатель может послать отправителю ответ (см. Рисунок 2). SOAP предусматривает любое количество схем обмена сообщениями, запрос/ответ является всего лишь одним из них. Другие примеры включают требование/ответ (обратный запросу/ответу), нотификации и длительные одноранговые разговоры.
Рисунок 2. Схема обмена сообщениями запрос/ответ
Разработчики часто путают запрос/ответ с RPC, в то время как это совершенно разные вещи. RPC использует запрос/ответ, но запрос/ответ необязательны для RPC. RPC является моделью программирования, которая дает возможность разработчикам работать с вызовами методов. RPC требует преобразования сигнатуры метода в SOAP сообщения. Из-за популярности RPC SOAP описывает соглашение об использовании RPC с SOAP (см. раздел RPC и кодирование данной статьи)
Вооруженная этими тремя главными характеристиками, оболочка обмена SOAP сообщениями способствует обмену XML сообщениями в гетерогенных средах, для которых возможность взаимодействия долгое время была проблемой.
От первой опубликованной спецификации SOAP до сегодняшней широко применяемой SOAP 1.1 многое изменилось: начиная от малейших деталей, заканчивая значительными подвижками в мышлении. SOAP 1.1 была предложена W3C и опубликована как заметка в мае 2000. Статус "Заметка W3C" сделало SOAP 1.1 несколько большим, чем хорошей идеей, т.к. она не прошла строгости обработки W3C, по окончании которого окончательно достигла бы статуса "Рекомендации". Тем не менее, из-за широкой поддержки как крупных, так и мелких производителей, сегодня SOAP 1.1, фактически, все еще считается стандартом.
W3C использовал Заметку SOAP 1.1 в качестве источника для новой Рабочей группы XML протокола, ответственной за создание следующей версии SOAP, SOAP 1.2. Сейчас SOAP 1.2 является "Кандидат в Рекомендации", это означает, что она находится на стадии реализации и недалека от завершения. Как только SOAP 1.2 станет "Рекомендацией", она, вероятно, быстро найдет поддержку у производителей.
После введения SOAP 1.2 производители должны будут продолжать поддерживать SOAP 1.1 для обеспечения обратной совместимости. Разработка новых версий SOAP основывается на пространствах имен XML. SOAP 1.1 определяется пространством имен http://schemas.xmlsoap.org/soap/envelope/, в то время как SOAP 1.2 – пространством имен http://www.w3.org/2002/12/soap-envelope (хотя оно изменится, когда SOAP 1.2 станет Рекомендацией).
В Таблице 1 представлены пространства имен и местоположения спецификаций каждой версии. Далее статья будет посвящена наиболее важным вопросам SOAP 1.1. В текущей спецификации SOAP 1.2 вы можете найти подробный список различий между этими двумя версиями.
Таблица 1. Информация по SOAP версиям
SOAP 1.1 | |
Имя пространства имен | http://schemas.xmlsoap.org/soap/envelope/ |
Местоположение спецификации | http://www.w3.org/TR/SOAP/ |
SOAP 1.2 | |
Имя пространства имен | http://www.w3.org/2002/12/soap-envelope |
Местоположение спецификации |
http://www.w3.org/TR/soap12-part0/ http://www.w3.org/TR/soap12-part1/ http://www.w3.org/TR/soap12-part2/ |
Основной раздел спецификации SOAP – это оболочка обмена сообщениями. Оболочка обмена сообщениями SOAP определяет набор элементов XML для организации пакетов произвольных XML сообщений для передачи их между системами.
Оболочка состоит из следующих основных XML элементов: Envelope, Header, Body и Fault. Все они из пространства имен http://schemas.xmlsoap.org/soap/envelope/ SOAP 1.1. Я привел здесь полное описание XML Schema для SOAP 1.1. Лично для себя я нашел полезным просмотр схемы при ознакомлении с конструкциями XML.
Описание XML Schema SOAP 1.1
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://schemas.xmlsoap.org/soap/envelope/"
targetNamespace="http://schemas.xmlsoap.org/soap/envelope/"
>
<!-- Envelope, header and body -->
<xs:element name="Envelope" type="tns:Envelope" />
<xs:complexType name="Envelope" >
<xs:sequence>
<xs:element ref="tns:Header" minOccurs="0" />
<xs:element ref="tns:Body" minOccurs="1" />
<xs:any namespace="##other" minOccurs="0"
maxOccurs="unbounded" processContents="lax" />
</xs:sequence>
<xs:anyAttribute namespace="##other"
processContents="lax" />
</xs:complexType>
<xs:element name="Header" type="tns:Header" />
<xs:complexType name="Header" >
<xs:sequence>
<xs:any namespace="##other" minOccurs="0"
maxOccurs="unbounded" processContents="lax" />
</xs:sequence>
<xs:anyAttribute namespace="##other"
processContents="lax" />
</xs:complexType>
<xs:element name="Body" type="tns:Body" />
<xs:complexType name="Body" >
<xs:sequence>
<xs:any namespace="##any" minOccurs="0"
maxOccurs="unbounded" processContents="lax" />
</xs:sequence>
<xs:anyAttribute namespace="##any"
processContents="lax" />
</xs:complexType>
<!-- Global Attributes -->
<xs:attribute name="mustUnderstand" default="0" >
<xs:simpleType>
<xs:restriction base='xs:boolean'>
<xs:pattern value='0|1' />
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="actor" type="xs:anyURI" />
<xs:simpleType name="encodingStyle" >
<xs:list itemType="xs:anyURI" />
</xs:simpleType>
<xs:attribute name="encodingStyle"
type="tns:encodingStyle" />
<xs:attributeGroup name="encodingStyle" >
<xs:attribute ref="tns:encodingStyle" />
</xs:attributeGroup>
<xs:element name="Fault" type="tns:Fault" />
<xs:complexType name="Fault" final="extension" >
<xs:sequence>
<xs:element name="faultcode" type="xs:QName" />
<xs:element name="faultstring" type="xs:string" />
<xs:element name="faultactor" type="xs:anyURI"
minOccurs="0" />
<xs:element name="detail" type="tns:detail"
minOccurs="0" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="detail">
<xs:sequence>
<xs:any namespace="##any" minOccurs="0"
maxOccurs="unbounded" processContents="lax" />
</xs:sequence>
<xs:anyAttribute namespace="##any"
processContents="lax" />
</xs:complexType>
</xs:schema>
Если вы закончили рассмотрение complexType для Envelope, вы можете быстро ознакомиться с тем, какое отношение эти элементы имеют друг к другу. Следующий шаблон сообщения иллюстрирует структуру Конверта SOAP:
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header> <!-- optional -->
<!-- header blocks go here... -->
</soap:Header>
<soap:Body>
<!-- payload or Fault element goes here... -->
</soap:Body>
</soap:Envelope>
Элемент Envelope всегда является корневым элементом SOAP сообщения. Таким образом, приложения могут, просто взглянув на имя корневого элемента, распознать "SOAP сообщения". Приложения также могут определить версию используемого SOAP, проверяя имя пространства имен элемента Envelope.
Элемент Envelope содержит необязательный элемент Header (более подробно см. в разделе Расширяемость), за которым следует обязательный элемент Body. Элемент Body представляет полезный груз сообщения. Элемент Body является контейнером, в котором может содержаться любое количество элементов из любого пространства имен. Именно здесь размещаются данные, которые вы пытаетесь отправить.
Например, следующее SOAP сообщение представляет запрос для трансфара фондов между банковскими счетами:
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<x:TransferFunds xmlns:x="urn:examples-org:banking">
<from>22-342439</from>
<to>98-283843</to>
<amount>100.00</amount>
</x:TransferFunds>
</soap:Body>
</soap:Envelope>
Если получатель поддерживает запрос/ответ и может успешно обработать сообщение, он отправит другое SOAP сообщение назад отправителю. В этом случае информация ответа также будет содержаться в элементе Body, как показано в данном примере:
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<x:TransferFundsResponse
xmlns:x="urn:examples-org:banking">
<balances>
<account>
<id>22-342439</id>
<balance>33.45</balance>
</account>
<account>
<id>98-283843</id>
<balance>932.73</balance>
</account>
</balances>
</x:TransferFundsResponse>
</soap:Body>
</soap:Envelope>
Оболочка обмена сообщениями также определяет элемент Fault для представления ошибок в пределах элемента Body, когда что-то идет не так. Это важно, потому что без стандартного представления ошибки каждому приложению придется вводить собственные, что сделает невозможным для общей инфраструктуры различить успех и неудачу. Следующий пример SOAP сообщения содержит элемент Fault, который представляет ошибку "Несоответствующие фонды", происходящую при обработке запроса:
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>Insufficient funds</faultstring>
<detail>
<x:TransferError xmlns:x="urn:examples-org:banking">
<sourceAccount>22-342439</sourceAccount>
<transferAmount>100.00</transferAmount>
<currentBalance>89.23</currentBalance>
</x:TransferError>
</detail>
</x:TransferFunds>
</soap:Body>
</soap:Envelope>
Элемент Fault должен содержать элемент faultcode, за которым следует элемент faultstring. Элемент faultcode, используя имя определенного пространства имен, классифицирует ошибку, в то время как элемент faultstring обеспечивает читабельное описание ошибки для человека (подобно тому, как работает HTTP). В Таблице 2 приведены краткие описания определенных в SOAP 1.1 кодов ошибок (все они в пространстве имен http://schemas.xmlsoap.org/soap/envelope/)
Элемент Fault также может содержать элемент detail для предоставления деталей ошибки, которые могут помочь клиентам диагностировать проблему, особенно в случае кодов ошибки Client и Server.
Таблица 2. Коды ошибок в SOAP 1.1
Name | Meaning |
VersionMismatch | Обрабатывающая сторона обнаружила неверное пространство имен для SOAP элемента Envelope. |
MustUnderstand | Ближайший дочерний элемент SOAP элемента Header, который был или не понят, или не подходил обрабатывающей стороне, содержит SOAP атрибут mustUnderstand со значением "1". |
Client | Класс ошибок Client показывает, что сообщение было неправильно сформировано или не содержит соответствующей информации для того, чтобы быть успешно обработанным. Это обычно свидетельствует о том, что не надо повторно отправлять сообщение, не изменив его. |
Server | Класс ошибок Server показывает, что сообщение не может быть обработано не из-за его содержимого, но, скорее, из-за сбоя при обработки сообщения. Сообщение может пройти успешно, если будет повторно отправлено в более поздний момент времени. |
Теперь представьте, что вы хотите в оригинальное сообщение добавить некоторую идентификационную информацию, чтобы получатель мог определить, имеет ли отправитель соответствующие права на выполнение пересылки. Чтобы сделать это, необходимо добавить удостоверяющую информацию в Body, как показано ниже:
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<x:TransferFunds xmlns:x="urn:examples-org:banking">
<from>22-342439</from>
<to>98-283843</to>
<amount>100.00</amount>
<!-- security credentials -->
<credentials>
<username>dave</username>
<password>evad</password>
</credentials>
</x:TransferFunds>
</soap:Body>
</soap:Envelope>
Этот путь требует, чтобы каждая операция, требующая идентификации, работала с удостоверениями. Это также означает, что другие приложения при необходимости обеспечения безопасности должны разрабатывать свои собственные решения проблемы; разумеется, страдает возможность взаимодействия. Для общих нужд, таких как безопасность, больше смысла имеет определить стандартные SOAP заголовки, которые будут со всеми согласованы. Затем производители смогут встроить поддержку для расширенных функциональных возможностей в свою инфрастуктуру SOAP, и все будут в выигрыше. Этот подход увеличивает производительность разработчика и, в то же время, помогает обеспечить более высокий уровень взаимодействия. Это именно то, для облегчения чего была разработана модель расширяемости SOAP.
Большинство существующих протоколов делают различие между управляющей информацией (т.е. заголовками) и полезной информацией сообщения. В этом отношении SOAP ничем не отличается. SOAP элементы Header и Body обеспечивают те же различия в легкообрабатываемом мире XML. Кроме простоты в использовании, ключевым преимуществом расширяемого Envelope является то, что он может использоваться любым протоколом связи.
Заголовки всегда играли важную роль в протоколах, таких как HTTP, SMTP и др., потому что они дают возможность приложениям на обоих концах провода обсуждать поведение поддерживаемых команд. Хотя сама спецификация SOAP не определяет встроенные заголовки, со временем они будут играть в SOAP такую же важную роль. Стандартизация GXA и заголовков SOAP облегчит разработчикам определение протоколов богатых приложений без необходимости каждый раз изобретать колесо.
Элемент Header, так же как и элемент Body, является контейнером для управляющей информации. Он может содержать любое количество элементов из любого пространства имен (не из пространства имен SOAP). На элементы, помещенные в элемент Header, ссылаются как на блоки заголовка. Как и в других протоколах, блоки заголовка должны содержать информацию, оказывающую влияние на обработку полезной информации. Таким образом, это подходящее место для размещения чего-то, вроде элемента credentials, который помогает контролировать доступ к операции:
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<!-- security credentials -->
<s:credentials xmlns:s="urn:examples-org:security">
<username>dave</username>
<password>evad</password>
</s:credentials>
</soap:Header>
<soap:Body>
<x:TransferFunds xmlns:x="urn:examples-org:banking">
<from>22-342439</from>
<to>98-283843</to>
<amount>100.00</amount>
</x:TransferFunds>
</soap:Body>
</soap:Envelope>
Блоки заголовка также могут быть аннотированы глобальным SOAP атрибутом mustUnderstand, чтобы обозначить необходимость понимания заголовка получателем до обработки сообщения. Следующий пример иллюстрирует, как потребовать обработку заголовка credentials:
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<!-- security credentials -->
<s:credentials xmlns:s="urn:examples-org:security"
soap:mustUnderstand="1"
>
<username>dave</username>
<password>evad</password>
</s:credentials>
</soap:Header>
...
Если блок заголовка аннотирован mustUnderstand="1", и получатель не поддерживает данный заголовок, сообщение не будет обработано и отправителю будет возвращен Fault (с кодом состояния soap:MustUnderstand). Когда mustUnderstand="0" или этого атрибута нет, получатель может игнорировать эти заголовки и продолжать обработку. Атрибут mustUnderstand играет центральную роль во всей модели обработки SOAP.
SOAP определяет модель обработки, которая содержит основные правила обработки SOAP сообщений по мере их следования от SOAP отправителя к SOAP получателю. Рисунок 1 иллюстрирует простейший сценарий обмена SOAP сообщениями, в котором одно приложение (SOAP отправитель) посылает SOAP сообщение другому приложению (SOAP получателю).
Однако модель обработки допускает и более интересные архитектуры, подобные приведенной на Рисунке 3, которая содержит множество промежуточных узлов. Далее я буду использовать термин SOAP узел для обозначения любого приложения, которое обрабатывает SOAP сообщения, независимо от того, является ли оно начальным отправителем, промежуточным или конечным получателем; в противном случае я буду точен и буду использовать конкретный термин.
Рисунок 3. Сложный обмен SOAP сообщениями
Посредник располагается между начальным отправителем и конечным получателем и перехватывает SOAP сообщения. Посредник работает одновременно и как SOAP отправитель, и как SOAP получатель. Промежуточные узлы делают возможным разрабатывать некоторые интересные и гибкие сетевые архитектуры, на которые можно воздействовать содержимым сообщения. Маршрутизация SOAP сообщений является хорошим примером того, что значительно усиливает значимость SOAP посредников (более подробно о маршрутизации SOAP сообщений см. в Routing SOAP Messages with Web Services Enhancements 1.0)
Обрабатывая сообщение, SOAP узел берет на себя одну или более ролей, которые влияют на то, как обрабатываются SOAP заголовки. Ролям даются уникальные имена (в форме URI), таким образом, они могут быть идентифицированы во время обработки. SOAP узел, получив сообщение для обработки, сначала должен определить, какие роли он будет выполнять. Для этого он может проверить SOAP сообщение.
Как только он определится с ролями, которые будет выполнять, SOAP узел должен обработать все обязательные заголовки (отмеченные mustUnderstand="1"), направленные на одну из его ролей. Также SOAP узел может выбрать для обработки любой необязательный заголовок (отмеченный mustUnderstand="0"), направленный на одну из его ролей.
SOAP 1.1 определяет только одну роль - http://schemas.xmlsoap.org/soap/actor/next (next для краткости). Каждый SOAP узел должен принять роль next. Таким образом, когда SOAP сообщение приходит в любой данный SOAP узел, узел должен обработать все обязательные заголовки, нацеленные на роль next, и он может обработать необязательные заголовки, также нацеленные на роль next. Кроме next, SOAP 1.2 определяет еще несколько ролей (см. Таблица 3), приложениям разрешено также определять собственные роли.
SOAP заголовки нацеливаются на конкретные роли с помощью глобального атрибута actor (в SOAP 1.2 этот атрибут назван role). Если этого атрибута нет, заголовок по умолчанию нацеливается на конечного получателя. Следующее SOAP сообщение иллюстрирует, как использовать actor:
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<wsrp:path xmlns:wsrp="http://schemas.xmlsoap.org/rp"
soap:actor="http://schemas.xmlsoap.org/soap/actor/next"
soap:mustUnderstand="1"
>
...
Поскольку заголовок wsrp:path нацелен на роль next и помечен как обязательный (mustUnderstand="1"), первый SOAP узел, получающий это сообщение, должен обработать его в соответствии со спецификацией блока заголовка, в данном случае WS-Routing. Если SOAP узел разработан так, что не понимает обязательного заголовка, нацеленного на одну из его ролей, он должен сгенерировать SOAP ошибку с кодом состояния soap:MustUnderstand и прервать обработку. Чтобы определить, что вызвало ошибку в прохождении сообщения, элемент SOAP Fault предоставляет дочерний элемент faultactor. Значением атрибута faultactor является URI, который идентифицирует SOAP узел, вызвавший ошибку.
Если SOAP узел успешно обработал заголовок, требуется удалить этот заголовок из сообщения. SOAP узлам разрешено повторно вставлять заголовки, но это изменяет участников контракта – теперь заголовки targets между текущим и следующим узлами. Если SOAP узел оказывается конечным получателем, он также должен обработать и SOAP тело.
Таблица 3. Роли SOAP 1.2
Имя SOAP роли | Описание |
http://www.w3.org/2002/06/soap-envelope/role/next | Каждый SOAP посредник и конечный SOAP получатель ДОЛЖНЫ выполнять эту роль и МОГУТ дополнительно принимать ноль или более других SOAP ролей. |
http://www.w3.org/2002/06/soap-envelope/role/none | SOAP узлы НЕ ДОЛЖНЫ выполнять эту роль. |
http://www.w3.org/2002/06/soap-envelope/role/ultimateReceiver | Чтобы объявить себя конечным SOAP получателем, SOAP узел ДОЛЖЕН выполнять эту роль. SOAP посредники НЕ ДОЛЖНЫ выполнять эту роль. |
Интересно, что на Рисунке 3 SOAP обеспечивает обмен сообщениями через различные базовые протоколы. Поскольку оболочка обмена SOAP сообщениями не зависит от базовых протоколов, каждый посредник может использовать различные протоколы обмена без влияния на SOAP сообщения. Однако для обеспечения высоких уровней взаимодействия между SOAP приложениями и инфраструктурой необходимы стандартные взаимодействия протоколов.
Конкретное взаимодействие протоколов определяет как именно SOAP сообщения должны передаваться с помощью данного протокола. Иначе говоря, он определяет детали того, как SOAP согласовывается в рамках другого протокола, который, возможно, имеет собственную оболочку обмена сообщениями наряду с разнообразием заголовков. Что действительно определяет взаимодействие протоколов в большой степени зависит от характеристик и возможностей протокола. Например, взаимодействие протоколов для TCP будет сильно отличаться от взаимодействия для MSMQ или SMTP.
Спецификация SOAP 1.1 кодифицирует взаимодействие протоколов только для HTTP из-за его широкого распространения. SOAP используется и с другими протоколами, но реализации не следовали стандартизованному взаимодействию.
Взаимодействие протокола HTTP определяет правила использования SOAP поверх HTTP. SOAP запрос/ответ просто преобразовывается в модель HTTP запроса/ответа. Рисунок 4 иллюстрирует многие детали соединения SOAP HTTP.
Рисунок 4. SOAP HTTP взаимодействие
Заголовок Content-Type для HTTP сообщений запроса и ответа должен быть text/xml (application/soap+xml в SOAP 1.2). Что касается сообщения запроса, оно должно использовать в качестве команды слово POST, и URI должен определять SOAP обработчик. Спецификация SOAP также определяет новый HTTP заголовок – SOAPAction, который должен присутствовать во всех SOAP HTTP запросах (даже если они пустые). Заголовок SOAPAction предназначен для выражения цели сообщения. Что касается HTTP ответа, он должен использовать код состояния 200, если не произошла ошибка, или 500, если тело содержит SOAP Fault.
Хотя спецификация SOAP развивалась в сторону от объектов, она до сих пор определяет соглашение для инкапсуляции и обмена RPC вызовами с использованием оболочки обмена сообщениями, описанной выше. Определение стандартного способа преобразования RPC вызовов в SOAP сообщения дает возможность инфраструктуре во время выполнения автоматически проводить преобразования между вызовами методов и SOAP сообщениями без доработки кода на платформе Web сервисов.
Чтобы сделать вызов метода, используя SOAP, инфраструктуре нужна следующая информация:
1. Местоположение (URI)
2. Имя метода
3. Имена/значения параметра
4. Необязательно сигнатура метода
5. Необязательно данные заголовка
Эта информация может переноситься различными способами, включая библиотеки типов, IDL файлы или лучше WSDL файлы. Взаимодействие SOAP RPC определяет, как инкапсулировать или представить эту информацию в SOAP теле. Сначала определяется, как сигнатура метода преобразовывается в простые структуры запроса/ответа, которые затем могут быть кодированы как XML. Соединение RPC устанавливает, что вызов метода будет сделан как struct, названная так же как и метод. Struct будет содержать аксессор для каждого параметра [in] или [in/out], названный так же как и параметр, и в последовательности, определенной сигнатурой сообщения. Ответ метода также будет смоделирован как struct. Имя struct может быть любым, хотя, согласно соглашению, надо использовать имя метода, оканчивающееся словом "Response" (т.е. для операции add ответ метода, соответственно, будет называться addResponse). Struct ответа содержит аксессор для возвращаемого значения (в SOAP 1.1 имя не имеет значения, но в SOAP 1.2 должно быть rpc:result), за которым следует аксессор для каждого параметра [out] или [in/out].
Давайте рассмотрим пример. Следующая сигнатура метода на C# допускается для операции add:
double add(ref double x, double y)
В соответствии с только что описанными правилами RPC соединения, struct запроса, представляющая вызов метода, будет смоделирована следующим образом:
struct add {
double x;
double y;
}
В то время как struct ответа будет выглядеть так:
struct addResponse {
double result;
double x;
}
Теперь вопрос в следующем: как должны эти структуры преобразовываться в XML? Спецификация SOAP именно для этих целей определяет ряд правил кодирования. Правила кодирования SOAP описывают, как преобразовывать наиболее часто используемые сегодня структуры данных (такие как structs и массивы) в общий XML формат. Согласно правилам кодирования SOAP, struct запроса из примера, приведенного выше, преобразуется в следующее XML сообщение (оно будет помещено в SOAP тело):
<add>
<x>33</x>
<y>44</y>
</add>
И ответное сообщение на этот запрос будет преобразован в XML сообщение (которое пойдет в тело ответного сообщения):
<addResponse>
<result>77</result>
<x>33</x>
</addResponse>
Правила SOAP кодирования были введены во время, когда работа над XML Schema только начиналась. Теперь эта XML Schema закончена, разработчики могут просто обеспечить литеральные описания XML Schema, которые точно определяют, как сообщения запроса/ответа должны форматироваться в XML. Из-за того, что, используя описания XML Schema, стало проще достигнуть возможности взаимодействовать, большинство разработчиков решили полностью отказаться от правил SOAP кодирования. Кстати, что касается SOAP 1.2, спецификацией больше официально не требуется поддержка правил SOAP кодирования. Дискуссия на эту тему, почему приведена в The Argument Against SOAP Encoding.
Хотя взаимодействие SOAP RPC и правила кодирования обеспечивают хороший уровень SOAP интеграции для приложений, которые не хотят копаться в таких вещах как XML Schema или WSDL, они сильно вышли за пределы интересов сообщества Web сервисов, потому что больше сместились в сторону вопросов взаимодействия.
Повторим, сегодня существует два основных стиля обмена SOAP сообщениями: документ и RPC. Стиль документ свидетельствует о том, что тело просто содержит XML документ, формат которого отправитель и получатель должны согласовать. С другой стороны, стиль RPC свидетельствует о том, что тело содержит XML представление вызова метода, как мы только что обсудили.
Также есть две техники для решения того, как сериализовать данные в тело: используя литеральные описания XML Schema и используя правила SOAP кодирования. В первом подходе описание схемы литерально определяет XML формат для тела без неоднозначностей. Во втором подходе, однако, SOAP обработчик должен во время выполнения перебрать различные правила SOAP кодирования, чтобы найти подходящую сериализацию для тела. Очевидно, что эта техника более подвержена ошибкам и проблемам взаимодействия.
Чаще всего используется стиль документа с литеральными описаниями схемы (известен как документ/литеральный) и RPC стиль с правилами SOAP кодирования (известен как rpc/кодированный). Документ/кодированный и rpc/литеральный возможны, но они не общеприняты и не имеют особого смысла. Документ/литеральный – это стиль, на котором сфокусировано большинство платформ Web сервисов, и сегодня он применяется по умолчанию в оболочке WebMethod Microsoft® ASP.NET.
SOAP определяет простую и наращиваемую оболочку обмена XML сообщениями, которая может использоваться во многих протоколах с разнообразными моделями программирования, несмотря на то, что спецификация описывает только то, как использовать SOAP с HTTP и RPC вызовами. SOAP также определяет полную модель обработки, которая описывает механизм обработки сообщений. В целом, SOAP предоставляет богатую и гибкую оболочку для определения протоколов высокоуровневых приложений, которые предлагают улучшенную возможность взаимодействия в распределенной, гетерогенной среде.