Страница 2 из 3
SOAP работает очень хорошо, если о Web-службе все известно. Однако это не всегда так. Средством описания интерфейса для доступа к Web-службе является язык WSDL (Web Services Description Language - язык описания Web-служб). Этот стандарт совместно разработан компаниями IBM, Microsoft и webMethods. У каждой из этих трех компаний был собственный подход к разработке стандарта для описания Web-служб: IBM создала NASSL, Microsoft разработала SCL, а компания webMethods придумала WIDL.
Результатом их сотрудничества стала версия 1.1 языка WSDL, По поводу W3C следует отметить, что так же как и с SOAP, консорциум W3C на основе версии 1.1 разработал версию WSDL 1.2, которая теперь является рекомендацией W3C. WSDL-описание Web-службы содержит всю необходимую для использования этой службы информацию, включая доступные методы и их параметры. Эта информация содержится в следующих пяти элементах:
Вся эта информация хранится в корневом элементе WSDL-описания
Да уж... без стаканА не разберёшся, а ведь это один из самых простеньких(!) WSDL файлов. К сожалению, один из недостатков SOAP-расширения для РНР 5 связан с тем, что в отличие от других реализаций SOAP, оно не позволяет создавать WSDL-описания автоматически (во всяком случае, пока что). Наверняка этот недостаток исправят в будущих версиях РНР.
Кстати!
Для автоматического создания WSDL-описания вы можете использовать альтернативные реализации протокола SOAP в РНР:
Теперь, после того как мы знаем, как получать информацию о Web-службе и как ее запрашивать, нужно научиться находить такую службу. Для этой цели существует нечто похожее на "Желтые страницы", а именно - реестры UBR (Universal Business Registries - универсальные бизнес-реестры) - справочники Web-служб.
Существует несколько таких реестров, среди которых реестры компаний IBM, Microsoft, NTT-Com и SAP. Эти реестры синхронизируют свои данные, поэтому можно пользоваться любым из них. Текущей версией стандарта UDDI является версия UDDI 3.0, хотя большинство реализаций используют версию 2. Среди разработчиков этого стандарта такие компании-гиганты, как HP, Intel, Microsoft и Sun.
Для взаимодействия с UBR существует два типа API-интерфейсов: Inquiry API и Publish API . Интерфейс Inquiry API (Запрос) предназначен для запроса служб в реестрах UBR, а интерфейс Publish API (Публикация) позволяет разработчикам регистрировать свои службы . Похоже, что заполнение содержимого реестров спамом - это только вопрос времени:)
Кстати!
Существуют тестовые реестры, предназначенные для тестирования регистрации служб перед их размещением в "настоящих" реестрах.
В примере выше видно, что UDDI-запрос инкапсулирован в SOAP-сообщение, поэтому выглядит он довольно знакомым. Ответом на запрос является также SOAP-документ, показанный ниже:
Установить SOAP-расширение для PHP5 довольно легко. В Windows этот модуль находится в подкаталоге ext каталога установки РНР. Для его использования необходимо в файл php.ini добавить следующую строку: extension=php_soap.dll Для работы этому модулю требуется, которая включена в РНР 5 по умолчанию, по крайней мере, в Windows-версии.
Как WSDL 1.1 определяет Web-сервисы, и как создать модели на языке Java для верификации и преобразования WSDL-документов
Web-сервисы ― важная функция технологии Java™ в корпоративных вычислениях. В этом цикле статей консультант по XML и Web-сервисам Денис Сосновский рассказывает об основных структурах и технологиях, ценных для Java-разработчиков, использующих Web-сервисы. Следите за статьями цикла, чтобы быть в курсе последних разработок в данной области и знать, как применить их в своих собственных проектах.
Web-сервисы для корпоративных приложений в значительной степени зависят от использования определений сервисов. Определения сервисов описывают основное соглашение между поставщиком услуг и любым потенциальным потребителем, детализируя типы функций, предоставляемых сервисом, и сообщения в рамках каждой функции. Поставщики и потребители свободны в выборе способа реализации своих объектов обмена в той мере, в какой фактические сообщения, которые они посылают, соответствуют определению сервиса. Использование определения сервиса с описанием способа обмена XML-сообщениями ― это то, что отличает Web-сервисы от более ранних технологий распределенного программирования.
Предлагались различные методы определения Web-сервисов, но наиболее широко используемым подходом остается WSDL 1.1. WSDL 1.1 имеет некоторые недостатки, в том числе чрезмерно сложную структуру, которая затрудняет его чтение для непосвященных. Он страдает также от отсутствия авторитетного формального определения, что привело к последовательным «уточнениям», которые заполняют некоторые пробелы в исходном документе спецификации. В результате стеки Web-сервисов стараются обрабатывать документы WSDL 1.1 как можно более гибко. Эта гибкость может усилить путаницу в понимании WSDL 1.1, так как разработчики видят широкий спектр WSDL-структур без каких-либо указаний на то, какой подход предпочтительнее.
В этой статье мы покажем, как разобрать документы WSDL 1.1, и рассмотрим первые части модели Java для проверки WSDL-документов и их преобразования в стандартную форму.
В этой статье используются:
Редакция 1.1 WSDL, опубликованная в начале 2001 года, технически заменена рекомендациями W3C WSDL 2.0, опубликованными в 2007 году. WSDL 2.0 предлагает более четкую структуру, чем WSDL 1.1, наряду с большей гибкостью. Но WSDL 2.0 страдает от проблемы курицы и яйца: WSDL 2.0 не используется широко, потому что не поддерживается широко, а так как он широко не используется, у разработчиков стеков Web-сервисов мало стимулов его поддерживать. Несмотря на все его недостатки, для большинства целей WSDL 1.1 достаточно хорош.
Оригинальная спецификация WSDL 1.1 была неточной в отношении количества используемых функций. Так как в центре внимания WSDL была работа с определениями служб SOAP, он включал также поддержку функций SOAP (таких как кодирование rpc), которые позднее оказались нежелательными. Организация Web Services Interoperability Organization (WS-I) решила эти проблемы в Базовом профиле (BP), который содержит практические рекомендации по Web-сервисам с использованием SOAP и WSDL. BP 1.0 был утвержден в 2004 году, а в 2006 году вышла редакция BP 1.1. В этой статье рассматривается WSDL 1.1 на базе рекомендаций BP WS-I и не затрагиваются фактически устаревшие функции, такие как кодирование rpc для SOAP.
Предполагается, что структура XML-документов задается определениями XML-схемы. В первоначальную спецификацию WSDL 1.1 входит описание схемы, но эта схема в нескольких отношениях не соответствует текстовым описаниям. Позже это было исправлено в модифицированной версии схемы, но документ WSDL 1.1 не был отредактирован с учетом этого изменения. Затем группа BP WS-I решила внести еще больше изменений в схему WSDL и создала то, что преподносится как практические рекомендации к этой скользкой схеме. Документы, написанные для одной версии схемы, как правило, не совместимы с другими версиями (несмотря на то, что используется одно и то же пространство имен), но к счастью, большинство инструментов Web-сервисов в основном игнорирует схему и принимает все, что выглядит разумным. (См. ссылки на многие схемы WSDL в разделе ).
Даже версия BP WS-I схемы WSDL 1.1 не очень помогает гарантировать соответствие спецификации документов WSDL 1.1. Схема не отражает всех ограничений BP WS-I, особенно в отношении порядка следования компонентов. Кроме того, XML-схема не способна обработать многие типы легко устанавливаемых ограничений в документах (такие как альтернативные атрибуты или необходимые дополнительные элементы из отдельной схемы). Поэтому проверка соответствия документа WSDL 1.1 спецификации WSDL 1.1 (с поправками, внесенными BP WS-I) включает в себя гораздо больше, чем просто выполнение валидации XML-схемы. Мы еще вернемся к данной теме в этой статье. Но сначала рассмотрим структуру описаний сервиса WSDL 1.1.
В документах WSDL 1.1 используется фиксированный корневой элемент с удобным названием
Существует также элемент
Для полного описания сервиса, как правило, требуется, по крайней мере, один элемент каждого из этих типов, за исключением
В листингах 1 и показан пример описания сервиса WSDL, разбитого на два WSDL-документа, так что компоненты описания интерфейса содержится в файле BookServerInterface.wsdl, а компоненты реализации ― в файле BookServerImpl.wsdl. В листинге 1 показан BookServerInterface.wsdl.
В листинге 2 показан BookServerImpl.wsdl. Элемент
Помимо определений элементов (и атрибутов) в пространстве имен WSDL 1.1, WSDL 1.1 определяет также дополнительные элементы. Они предназначены для заполнения конкретных ячеек в описаниях сервисов WSDL 1.1 для передачи дополнительной информации, необходимой для конкретного типа сервисов. Единственные дополнительные элементы WSDL 1.1, которые все еще широко используются, это привязки для SOAP 1.1 (они представлены в , в элементах
Элемент
Поскольку один элемент
Помимо
Сообщения, представленные элементами
Элементы
WSDL 1.1 определяет несколько шаблонов взаимодействия между клиентом и поставщиком услуг, представленных различными последовательностями дочерних элементов
Каждый элемент
Во многих отношениях
SOAP 1.1 широко используется для Web-сервисов с момента опубликования спецификации в 2000 году. Версия SOAP 1.2 разработана при более широкой поддержке отрасли через W3C и опубликована в качестве официального стандарта W3C в 2007 году. SOAP 1.2 лучше документирована и чище, чем SOAP 1.1, причем некоторые уродливые аспекты 1.1 хирургически удалены. Несмотря на эту очищенную структуру, для большинства Web-сервисов практических различий между ними немного. Вероятно, наиболее существенная особенность SOAP 1.2 заключается в том, что это единственный официально поддерживаемый способ использования расширенной поддержки SOAP-вложений XML-binary Optimized Packaging (XOP) и SOAP Message Transmission Optimization Mechanism (MTOM). В цикле Web-сервисы Java я до сих пор использовал SOAP 1.1, потому что некоторые старые стеки не поддерживают SOAP 1.2, но для разработки новых Web-сервисов 1.2, вероятно, является лучшим выбором.
Элементы
Дочерние элементы
Расширения, определяемые WSDL, вступают в игру в
Внутри каждого дочернего элемента
Последним компонентом описания сервиса WSDL является элемент
Не удивительно, что при всех вариациях схем и правил для документов WSDL 1.1 многие документы не соответствуют практическим рекомендациям BP WS-I. Поддержка всеми стеками Web-сервисов многих отклонений от практических рекомендаций помогла увековечить использование устаревших или неправильных конструкций, что привело к распространению дурной практики по всей отрасли. И я определенно не застрахован от этой инфекции – просматривая WSDL-документы, которые я приводил в качестве примеров кода для этого цикла, я к своему удивлению обнаружил, что ни один из них не является полностью корректным.
Так что когда я решил написать эту статью, я подумал, что было бы хорошо включить в нее инструмент, с помощью которого можно проверять WSDL-документы на соответствие практическим рекомендациям. Казалось бы, отсюда всего один шаг до преобразования WSDL-документов в правильную форму, при условии, что оригинальный WSDL свободен от ошибок. Но работы оказалось значительно больше, чем я первоначально планировал, и полную информацию об этой модели я включу в следующие две статьи этого цикла.
Для работы с WSDL-документами на языке Java построено множество различных моделей, в том числе широко используемый язык описания Web-сервисов для Java Toolkit (WSDL4J), который представляет собой эталонную реализацию JSR 110 (см. раздел ). Ни одна из этих моделей не соответствует тому, что я собирался сделать, ввиду двоякой постановки задачи: во-первых, чтение WSDL-документов в любой полуразумной форме и сообщение об ошибках и отклонениях от практических рекомендаций, и во-вторых, написание безошибочных WSDL-документов, переформатированных в форму, соответствующую практическим рекомендациям. WSDL4J, например, не сохраняет порядок вводимых элементов, так чтобы можно было сообщать о проблемах порядка их следования, и не обрабатывает определения схемы, так что его нельзя напрямую использовать для проверки ссылок из элементов
В этой статье я использую термин верификация для обозначения проверки правильности WSDL-документа, потому что альтернативный термин валидация , обычно используемый для XML-документов, означает проверку документов на соответствие определению схемы.
Ранее я уже частично реализовал модель WSDL для использования с привязкой данных JiBX в рамках проекта JiBX/WS. Эта модель предназначена только для вывода и включает относительно небольшое число классов, которые в некоторых случаях объединяют данные из вложенных элементов структуры WSDL XML (
Еще один вариант ― генерирование кода из схемы BP WS-I для WSDL 1.1. Увидев это, я понял, что простое использование созданных классов напрямую приведет к путанице, так как схема включает избыточные типы, а также некоторые неудобные конструкции, которые используются для представления различных моделей обмена сообщениями (некоторые из которых затем были запрещены текстом BP WS-I).
Так что в конечном итоге я составил классы вручную, хотя результат оказался почти таким же, как если бы я начал с кода, сгенерированного из схемы, и просто сократил ненужное дублирование и сложность. Привязка данных JiBX поддерживает несколько связей с одними и теми же классами, так что мне удалось создать привязку ввода для обработки всего спектра вариантов, допускаемых любой версией WSDL 1.1, хотя настройка привязки выхода для вывода WSDL была только в форме, соответствующей практическим рекомендациям.
В листинге 3 показана часть класса Definitions , соответствующая корневому элементу
Организация данных дочерних элементов в показывает, каким образом модель поддерживает как общую форму ввода, так и форму вывода в соответствии с практическими рекомендациями. Вместо единого списка дочерних элементов всех типов, используются отдельные списки для каждого типа. Привязка ввода JiBX обрабатывает дочерние элементы как неупорядоченный набор, вызывая специфический для данного типа элементов set-метод всякий раз, когда дочерний элемент находится не на своем месте. Вместо того чтобы заменять какое-либо из предшествующий значений, set-метод добавляет экземпляр в типизированный список, как видно из set-метода addMessage() , который используется для дочерних элементов
В любом из элементов WSDL разрешены дополнительные атрибуты и элементы (как правило, все атрибуты или элементы, которые не используют пространство имен WSDL 1.1). Примером таких дополнительных элементов служат конфигурации WS-Policy, встроенные в WSDL-документы из предыдущих статей данного цикла, как и ссылки на фактические политики. Лучше всего, чтобы эти дополнительные элементы предшествовали любым дочерним элементам из пространства имен WSDL 1.1, и именно так они обрабатываются в привязке вывода. Привязка ввода обрабатывает дополнительные элементы и атрибуты с помощью кода базового класса из классов элементов WSDL, не показанного в , и позволяет элементам следовать в любом порядке (генерируя предупреждение, если они следуют за элементом из пространства имен WSDL 1.1).
Модель обрабатывает известные элементы, используя отдельные привязки для каждого дополнительного пространства имен, каждая из которых имеет свой собственный набор классов. Я рассмотрю обработку этих дополнительных элементов более подробно в следующем выпуске Web-вервисов Java , где будет подробнее представлен и исходный код.
Некоторая базовая верификация данных WSDL выполняется по мере добавления немаршаллизованных объектов, соответствующих элементам, в древовидную структуру WSDL-документа, как показано в коде addMessage() в конце . Этот код использует метод checkAdd() для проверки порядка следования дочерних элементов и метод addName() для проверки того, что представлено допустимое имя (текст соответствует типу схемы NCName и значение уникально в пределах типа элемента), и отображения имени на объект. Но это только проверка самой основной информации для отдельного элемента; для проверки других свойств каждого элемента и взаимосвязей между элементами необходим дополнительный код верификации.
JiBX позволяет вызывать обработчики пользовательских расширений в рамках процесса маршаллинга и демаршаллинга. Для исполнения логики верификации модель WSDL использует один из таких дополнительных обработчиков, метод post-set. Метод post-set вызывается после завершения демаршаллинга связанного объекта, так что это часто хороший способ выполнения проверок типа верификации объектов. В случае проверки WSDL простейший подход – это выполнение всей верификации объектов из одного метода post-set для корневого элемента
В этой статье изложены основы структуры и использования WSDL и введение в модель данных Java для WSDL, предназначенную для поддержки верификации WSDL-документов и их преобразования в форму, соответствующую практическим рекомендациям.
Следующая статья продолжит эту тему, рассматривая проблемы, часто встречающиеся при написании утверждений WS-Policy и WS-SecurityPolicy. Кроме того, в ней будет более подробно рассмотрена модель WSDL и процесс верификации, в том числе расширение модели с включением утверждений WS-Policy/WS-SecurityPolicy, встроенных в WSDL.
Элементы расширения связывания используются для указания конкретной грамматики для входящих (3) и исходящих (4) сообщений, сообщений об ошибках (5). Также может указываться информация уровня операции (2) и уровня связывания (1).
Элемент связывания operation содержит данные для одноимённой операции связанного типа порта. Однако имя операции в общем случае не является уникальным (пример: перегрузка методов / функций — использование одинаковых имён с различными сигнатурами), потому его может быть недостаточно для однозначного определения целевой операции типа порта. В таких случаях целевая операция адресуется с помощью дополнительного задания соответствующих имён элементов wsdl:input и wsdl:output с помощью атрибута name .
Связывание должно устанавливать только один протокол.
Связывание не должно содержать информации об адресе.
Порт определяет отдельную конечную точку посредством установки адреса для связывания.
Атрибут name задаёт уникальное имя среди всех портов в рамках WSDL-документа. Атрибут binding типа QName содержит ссылку на связывание (см.).
Элементы расширения (1) используются для задания адреса.
Порт не должен задавать более одного адреса.
Порт не должен содержать любую информацию связывания, отличную от адреса.
Служба объединяет вместе набор связанных портов.
Атрибут name задаёт уникальное имя среди всех служб, определённых в рамках WSDL-документа.
Порты внутри службы связаны следующим образом:
Язык описания Web-сервисов (WSDL)
В последних нескольких примерах вы могли видеть отдельные фрагменты WSDL-кода. Напомним, что WSDL – это основанная на XML грамматика, предназначенная для описания возможностей взаимодействия внешних клиентов с Web-методами, доступными по данному адресу URL в рамках каждого из поддерживаемых протоколов связи. Во многих отношениях WSDL-документ может рассматриваться, как "контракт" между клиентом Web-сервиса и самим Web-сервисом. Это еще один метаязык. В частности, WSDL используется для описания следующих характеристик любого доступного Web-метода:
Имя Web-метода XML;
Число, тип и порядок следования параметров (если таковые имеются);
Тип возвращаемого значения (если таковое предусмотрено);
Условия вызова HTTP GET, HTTP POST и SOAP.
В большинстве случаев WSDL-документы генерируются автоматически соответствующим Web-сервером. Напомним, что при добавлении суффикса?wsdl к адресу URL, указывающему на файл *.asmx, Web-сервер генерирует WSDL-документ для указанного Web-сервиса XML.
http://locаlhost/SomeWS/theWS.asmx?wsdl
Но если IIS автоматически генерирует WSDL-документ для данного Web-сервиса XML, зачем тогда нужно глубокое понимание синтаксиса генерируемых WSDL-данных? Ответ обычно зависит от того, как ваш сервис будет использоваться внешними приложениями. В случае Web-сервисов XML, предназначенных для "внутреннего" использования, сгенерированного Web-сервером WSDL-кода будет, как правило, достаточно.
Между тем. вполне возможно начать разработку Web-сервиса XML с создания WSDL-документа вручную (об этом уже говорилось выше). Главная идея начала разработки с создания WSDL-документа связана с вопросами совместимости. Вспомните о том, что до появления спецификации WSI различные инструменты построения Web-сервисов нередко генерировали несовместимые WSDL-описания. Если начинать разработку с WSDL-кода, вы можете построить документ так, как требуется.
Как вы можете догадаться, для начала разработки Web-сервиса XML с создания WSDL-документа требуется очень хорошее знание грамматики WSDL, обсуждение которой в контексте этой главы не предусмотрено. Но мы рассмотрим базовую структуру WSDL-документа. Разобравшись в основах, вы сможете оценить пользу утилиты командной строки wsdl.exe.
Замечание. Самую свежую информацию о языке WSDL можно найти на страницах http://www.w3.org/tr/wsdl .
Определение WSDL-документа
Действительный документ WSDL открывается и закрывается корневым элементом ‹definitions›. В открывающем дескрипторе обычно определяются различные атрибуты xmlns. Они задают пространства имен XML, определяющие различные подчиненные элементы. Как минимум, элемент ‹definitions› должен указать пространство имен, где определены сами элементы WSDL (http://schemas.xmlsoap.org/wsdl). Для того чтобы быть полезным, открывающий дескриптор ‹definitions› должен, кроме того, указать пространства имен XML, определяющие простые типы данных WSDL, типы XML-схемы, элементы SOAP, а также целевое пространство имен. Например, вот как выглядит раздел ‹definitions› для нашего Web-сервиса калькулятора.
‹wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns-mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:tns="http://www.IntertechTraining.com/"
xmlns:s="http://www.w3.org/2001/XMLSchema"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:http="http://schemes.xmlsoap.оrg/wsdl/http/"
targetNamespace="http://www.IntertechTraining.com/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"›
‹/wsdl :definitions›
В контексте корневого элемента вы можете найти пять подчиненных элементов. Общий вид WSDL-документа должен быть примерно таким.
‹?xml version="1.0" encoding="utf-8"?›
‹wsdl:definitions …›
‹wsdl:types›
‹!-- Список типов, доступных для данного Web-сервиса - -›
‹wsdl:/types›
‹wsdl:message›
‹!-- Формат сообщений - -›
‹wsdl:/message›
‹wsdl:portType›
‹!-- Информация портов - -›
‹wsdl:/portType›
‹wsdl:binding›
‹!-- Информация связывания - -›
‹wsdl:/binding›
‹wsdl:service›
‹!-– Информация о самом Web-сервисе XML - -›
‹wsdl:/service›
‹wsdl:/definitions›
Как и следует ожидать, каждый из этих подчиненных элементов будет содержать дополнительные элементы и атрибуты, уточняющие описание имеющихся возможностей. Давайте по очереди рассмотрим наиболее важные из допустимых узлов.
Элемент ‹types›
Сначала мы рассмотрим элемент ‹types›, который содержит описания всех типов данных, предлагаемых Web-сервисом. Вы, возможно, знаете, что язык XML сам определяет ряд "базовых" типов данных, и все они определены в рамках пространства имен XML http://www.w3.org/2001/XMLSchema (которое должно быть указано в контексте корневого элемента ‹definitions›). Возьмем, например, метод Subtract() нашего Web-сервиса калькулятора, имеющий два входных параметра целочисленного типа. В терминах WSDL тип System.Int32 среды CLR описывается в контексте элемента ‹complexType›.
‹s:еlement name= "Subtract"›
‹s:sequence›
‹s:element minOccurs="1 " maxOccurs="1 " name="x " type="s:int " /›
‹s:element minOccurs=""1 " maxOccurs="1 " name="y " type="s:int " /›
‹/ s:sequence›
‹/s:complexType›
‹/s:element›
Целое число, возвращаемое методом Subtract(), также описывается в рамках элемента ‹types›.
‹s:element name= "SubtractResponse "›
‹s:complexType›
‹s:sequence›
‹s:element minOccurs="1 " maxOccurs= "1 " name="SubtractResult " type="s:int "/›
‹/s:sequence›
‹ /s:complexType›
‹/s:element›
Если вы имеете Web-метод, возвращающий или получающий пользовательские типы данных, они также появятся в контексте элемента ‹complexType›. Детали того, как с помощью Web-метода сделать доступными пользовательские типы данных.NET, мы рассмотрим позже. Для примера предположим, что вы определили Web-мeтод, возвращающий структуру с именем Point.
public struct Point {
public string pointName;
WSDL-описание для этой "сложной структуры" будет выглядеть примерно так.
‹s:complexType name="Point "›
‹s:sequence›
‹s:element minOccurs="1 " maxOccurs="1 " name="x " type="s:int " /›
‹s:element minOccurs="1 "" maxOccurs="1 " name="y " type= "s:int " /›
‹s:element minOccurs="0 " maxOccurs="1 " name="рointName " type="s:string " /›
‹/s:sequence›
‹/s:complexType›
Элемент ‹message›
Элемент ‹message› используется для определения формата обмена запросами и ответами данного Web-метода. Поскольку один Web-сервис позволяет передачу множества сообщений между отправителем и получателем, одному WSDL-документу позволяется определять множество элементов ‹message›. Как правило, в этих определениях используются типы, указанные в рамках элемента ‹types›.
Независимо от количества элементов ‹message›, определенных в документе WSDL, они обычно "присутствуют" парами. Первое определение представляет входной формат сообщения, а второе – выходной формат того же сообщения. Например, метод Subtract() Web-сервиса CalculatorWebService определяет следующие элементы ‹message›.
‹wsdl:message name="SubtractSoapIn "›
‹wsdl:part name="parameters" element="tns:Subtract" /›
‹/wsdl:message›
‹wsdl: message name="SubtractSoapOut "›
‹wsdl:part name="parameters" element="tns:SubtractResponse" /›
‹/wsdl:message›
Здесь вы видите только связь SOAP соответствующего сервиса. Как говорилось в начале этой главы, Web-сервисы XML могут вызываться с помощью SOAP или HTTP-методов GET и POST. Но если вы разрешите связь HTTP POST (соответствующие объяснения будут предложены позже), генерируемый WSDL-код должен продемонстрировать следующие данные ‹message›.
‹wsdl: message name="SubtractHttpPostIn "›
‹part name="n1" type="s:string" /›
‹part name="n2" type="s:string" /›
‹wsdl:/message›
‹wsdl:message name="SubtractHttpPostOut "›
‹part name="Body" element="s0:int" /›
‹wsdl:/message›
Элементы ‹message› сами по себе не слишком полезны. Однако на эти определения сообщений ссылаются другие части WSDL-документа.
Замечание. Не все Web-методы требуют и запроса, и ответа. Если Web-метод является "односторонним", для него необходим только элемент ‹message› запроса. Обозначить Web-метод, как односторонний, можно с помощью атрибута .
Элемент ‹portType›
Элемент ‹portType› определяет различные связи, которые могут возникать между клиентом и сервером, и каждая такая связь представляется вложенным элементом ‹operation›. Несложно догадаться, что самыми типичными операциями здесь должны быть SOAP, HTTP GET и HTTP POST. Однако есть и другие операции. Например, односторонняя операция позволяет клиенту отправить сообщение данному Web-серверу, но не получить ответ (это похоже на вызов метода без ожидания возвращаемого значения). Операция "требование-ответ" позволяет серверу отправить, запрос во время ответа клиента (что можно рассматривать, как дополнение операции "запрос-ответ").
Чтобы проиллюстрировать формат необязательного вложенного элемента ‹operation›, рассмотрим WSDL-определение для метода Subtract().
‹wsdl portType name="CalculatorWebServiceSoap "›
‹wsdl:operation name="Subtract "›
‹wsdl:input message="tns:SubtractSoapIn " /›
‹wsdl:output message="tns:SubtractSoapOut " /›
‹ /wsdl:operation›
‹wsdl:/portType›
Обратите внимание на то, как элементы ‹input› и ‹output› ссылаются на соответствующее имя сообщения, определенное в рамках элемента ‹message›. Если бы для метода Subtract() был разрешен HTTP-метод POST, вы бы увидели следующий дополнительный элемент ‹operation›.
‹wsdl:portType name="CalculatorWebServiceHttpPost"›
‹wsdl:input message="s0:SubtractHttpPostIn
" /› ‹wsdl:output message= "s0:SubtractHttpPostOut
" /› ‹
wsdl:/operation› ‹wsdl:/portType› Наконец, учтите то, что если данный Web-метод описан с помощью свойства Description, элемент ‹operation› будет содержать вложенный элемент ‹documentation›. Элемент ‹binding› Этот элемент указывает точный формат обмена GET, POST и SOAP. Это самый "многословный" из всех элементов, содержащихся в контексте корневого элемента ‹definition›. Вот, например, определение элемента ‹binding› с описанием того, как вызывающая сторона может взаимодействовать с Web-методом MyMethod(). используя SOAP. ‹wsdl:binding name="СаlculatorWebServiceSoap12" type="tns:CalculatorWebServiceSoap
"› ‹soap12:binding transport="http://schemas.xmlsoap.org/soap/http
" /›
‹wsdl:operation name= "Subtract
"› ‹soap12:operation soapAction="http://www.IntertechTraining.com/Subtract
" style="document" /› ‹wsdl:input› ‹soap12:body use="literal
" /› ‹/wsdl:input› ‹wsdl:output› ‹soap12:body use="literal
" /› ‹/wsdl:output› ‹/wsdl:operation› ‹/wsdl:binding› Элемент ‹service› Наконец, у нас есть элемент ‹service›, который указывает характеристики самого Web-сервиса (например, его URL). Главной задачей этого элемента является описание множества портов, открытых данным Web-сервером. Для этого элемент ‹services› может использовать любое число вложенных элементов ‹port› (не путайте их с элементом ‹portType›). Вот как выглядит элемент ‹service› для CalculatorWebService. ‹wsdl:service name="CalculatorWebService
"› ‹wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"›
Чудесный Web-сервис калькулятора
‹/wsdl:documentation› ‹wsdl:port name="CalculatorWebServiceSoap"
binding="tns
:CalculatorWebServiceSoap"
› ‹soap:address location="http://localhost:1109/CalculatorWebService/ Service.asmx"
/› ‹/wsdl:port› ‹wsdl:port name="CalculatorWebServiceSoap12"
binding= "tns
:CalculatorWebServiceSoap12
"› ‹soap12:address location="http://localhost:1109/CalculatorWebService/Service.asmx"
/› ‹/wsdl:port› ‹/wsdl:service› Итак, как видите, WSDL-код, автоматически возвращаемый сервером ITS, не является сверхсложным, но, поскольку WSDL представляет собой грамматику на основе XML, этот код достаточно "многословен". Тем не менее, теперь вы должны лучше понимать роль WSDL, так что давайте рассмотрим немного подробнее протоколы связи Web-сервисов XML. Замечание.
Напомним, что пространство имен System.Web.Services.Description содержит множество типов, которые позволяют программно читать и обрабатывать "сырой" WSDL-код (можете проверить сами, если вас это интересует). Всем привет! Я надеюсь, что после прочтения топика вы сможете самостоятельно: И все же, я что-то забыл! Если еще немного порефлексировать, то стоит отметить, что клиент за раз может отправить на сервер как одно sms-сообщение, так и некоторое их количество. Другими словами, в одном пакете данных может быть от одного до бесконечности сообщений. В результате мы получаем, что для отправки sms-сообщения нам необходимы следующие данные: На первый вопрос мы ответили, теперь необходимо ответить на второй вопрос. И пожалуй, я позволю себе немного с халтурить. Поэтому, с сервера мы будем присылать только булевы данные, значение которых имеет следующий смысл: На этом мы закончили описание постановки задачи! И наконец-то приступим к самому интересному – будем разбираться что за диковинный зверь этот SOAP! И начну я свое повествование с того, что данный протокол обмена данными относится к подмножеству протоколов основанных на так называемой парадигме RPC (Remote Procedure Call, удалённый вызов процедур) антиподом которой является REST (Representational State Transfer, передача репрезентативного состояния). Более подробно об этом можно прочесть в Wikipedia, ссылки на статьи находятся в самом конце топика. Из этих статей нам надо уяснить следующее: «Подход RPC позволяет использовать небольшое количество сетевых ресурсов с большим количеством методов и сложным протоколом. При подходе REST количество методов и сложность протокола строго ограничены, из-за чего количество отдельных ресурсов может быть большим». Т.е., применительно к нам это означает, что на сайте в случае RPC подхода будет всегда один вход (ссылка) на сервис и какую процедуру вызывать для обработки поступающих данных мы передаем вместе с данными, в то время как при REST подходе на нашем сайте есть много входов (ссылок), каждая из которых принимает и обрабатывает только определенные данные. Если кто-то из читающих знает, как еще проще объяснить различие в данных подходах, то обязательно пишите в комментариях! Следующее, что нам надо узнать про SOAP – данный протокол в качестве транспорта использует тот самый XML, что с одной стороны очень хорошо, т.к. сразу же в наш арсенал попадает вся мощь стека технологий основанных на данном языке разметки, а именно XML-Schema – язык описания структуры XML-документа (спасибо Wikipedia!), который позволяет производит автоматическую валидацию поступающих на сервер данных от клиентов. И так, теперь мы знаем, что SOAP – протокол используемый для реализации удаленного вызова процедур и в качестве транспорта он использует XML! Если почитать статью на Wikipedia, то оттуда можно узнать еще и о том, что он может использоваться поверх любого протокола прикладного уровня, а не только в паре с HTTP (к сожалению, в данном топике мы будем рассматривать только SOAP поверх HTTP). И знаете, что мне во всем этом больше всего нравится? Если нет никаких догадок, то я дам подсказку – SOAP!… Всеравно не появилось догадок?… Вы точно прочли статью на Wikipedia?… В общем, не буду вас дальше мучить. Поэтому, сразу перейду к ответу: «SOAP (от англ. Simple Object Access Protocol - простой протокол
доступа к объектам; вплоть до спецификации 1.2
)». Самое примечательное в этой строчке выделено курсивом! Я не знаю какие выводы сделали вы из всего этого, но мне видится следующее – поскольку данный протокол ну никак нельзя назвать «простым» (и видимо с этим согласны даже в w3), то с версии 1.2 он вообще перестал как-то расшифровываться! И стал называться SOAP, просто SOAP и точка. Ну да ладно, прошу меня извинить, занесло немного в сторону. Как я писал ранее, в качестве транспорта используется XML, а пакеты, которые курсируют между клиентом и сервером называются SOAP-конвертами. Если рассматривать обобщенную структуру конверта, то он вам покажется очень знакомым, т.к. напоминает структуру HTML-страницы. В нем есть основной раздел – Envelop
, который включает разделы Header
и Body
, либо Fault
. В Body
передаются данные и он является обязательным разделом конверта, в то время как Header
является опциональным. В Header
может передаваться авторизация, либо какие-либо иные данные, которые на прямую не относятся к входным данным процедур веб-сервиса. Про Fault
особо рассказывать нечего, кроме того, что он приходит в клиент с сервера в случае возникновения каких-либо ошибок. На этом мой обзорный рассказ про протокол SOAP заканчивается (более детально сами конверты и их структуру мы рассмотрим когда наши клиент и сервер наконец-то научатся запускать их друг в друга) и начинается новый – про компаньона SOAP под названием WSDL
(Web Services Description Language). Да-да, это та самая штука, которая отпугивает большинство из нас от самой попытки взять и реализовать свое API на данном протоколе. В результате чего, мы обычно изобретаем свой велосипед с JSON в качестве транспорта. И так, что такое WSDL? WSDL – язык описания веб-сервисов и доступа к ним, основанный на языке XML (с) Wikipedia. Если из этого определения вам не становится понятным весь сакральный смысл данной технологии, то я попытаюсь описать его своими словами! WSDL предназначен для того, чтобы наши клиенты могли нормально общаться с сервером. Для этого в файле с расширением «*.wsdl» описывается следующая информация: Основная задачи схемы – описать структуру данных которые мы собираемся обрабатывать. Все данные в XML-схемах делятся на простые
(скалярные) и коплексные
(структуры) типы. К простым типам относятся такие типы как: Предлагаю далеко не ходить и написать XML-схему для нашего sms-сообщения! Ниже представлено xml-описание sms-сообщения: Думаю, что значение элементов «element
» и «complexType
» вам стало все более-менее понятно, поэтому не будем на них больше заострять внимание и переключимся сразу же на элемент-композитор «sequence
». Когда мы используем элемент-композитор «sequence
» мы сообщаем о том, что элементы включенные в него должны всегда располагаться в указанной в схеме последовательности, а также все из них являются обязательными. Но не стоит отчаиваться! В XML-схемах есть еще два элемента-композитора: «choice
» и «all
». Композитор «choice
» сообщает о том, что должен быть какой-то один из перечисленных в нем элементов, а композитор «all
» – любая комбинация перечисленных элементов. Как вы помните, то в первом разделе топика мы договорились о том, что в пакете может передаваться от одного до бесконечности sms-сообщений. Поэтому предлагаю разобраться как такие данные декларируются в XML-схеме. Общая структура пакета может выглядеть следующим образом: Второй блок схемы декларирует элемент «messageList
» типа «MessageList
». Видно, что «MessageList
» представляет собой комплексный тип, который включает минимум один элемент «message
», но максимальное число таких элементов не ограничено! Вообще, для того, чтобы у нас все работало правильно нам надо передавать клиенту WSDL-файл с правильным MIME-типом. Для этого необходимо настроить ваш веб-сервер соответствующим образом, а именно – установить для файлов с расширением «*.wsdl» MIME-тип равный следующей строке: Application/wsdl+xml
Header("Content-Type: text/xml; charset=utf-8");
Хочу сразу предупредить, наш простенький веб-сервис будет иметь довольно внушительное описание, поэтому не пугайтесь, т.к. большая часть текста является обязательной водой и написав ее один раз можно постоянно копировать от одного веб-сервиса к другому! Поскольку WSDL – это XML, то в самой первой строке необходимо прямо об этом и написать. Корневой элемент файла всегда должен называться «definitions
»:
После этого мы объявляем о том, что в нашем веб-сервисе «SmsService
» есть точка входа («port»), которая называется «SmsServicePort
». Именно в эту точку входа и будут отправляться все запросы от клиентов к серверу. И указываем в элементе «address
» ссылку на файл-обработчик, который будет принимать запросы. После того, как мы определили веб-сервис и указали для него точку входа – необходимо привязать к нему поддерживаемые процедуры:
После этого нам необходимо привязать процедуру к сообщениям:
Теперь нам необходимо описать сами сообщения, т.е. входящие и исходящие:
Теперь надо сделать самую малость – добавить описание данных типов в наш WSDL-файл! И как вы думаете, как описываются в WSDL входящие и исходящие данные? Думаю, что вы уже все давно поняли и сказали сами себе, что при помощи XML-схем! И вы будете абсолютно правы!
После того, как все было установлено и настроено нам необходимо будет создать в корневой папке вашего хостинга файл «smsservice.php
» со следующим содержанием: setClass("SoapSmsGateWay");
//Запускаем сервер
$server->handle();
Теперь мы подошли к серверу! Как видим, весь SOAP-сервер занимает всего лишь три строки! В первой строке мы создаем новый экземпляр объекта SoapServer и передаем ему в конструктор адрес нашего WSDL-описания веб-сервиса. Теперь мы знаем, что он будет располагаться в корне хостинга в файле с говорящим именем «smsservice.wsdl.php
». Во второй строке мы сообщаем SOAP-серверу какой класс необходимо дергать для того, чтобы обработать поступивший с клиента конверт и вернуть конверт с ответом. Как вы могли догадаться, именно в этом классе будет описан наш единственный метод sendSms
. В третьей строке мы запускаем сервер! Все, наш сервер готов! С чем я нас всех и поздравляю! Теперь нам необходимо создать WSDL-файл. Для этого можно либо просто скопировать его содержимое из предыдущего раздела, либо позволить себе вольности и немного его «шаблонизировать»: ";
?>
messageList = new MessageList();
$req->messageList->message = new Message();
$req->messageList->message->phone = "79871234567";
$req->messageList->message->text = "Тестовое сообщение 1";
$req->messageList->message->date = "2013-07-21T15:00:00.26";
$req->messageList->message->type = 15;
$client = new SoapClient("http://{$_SERVER["HTTP_HOST"]}/smsservice.wsdl.php",
array("soap_version" => SOAP_1_2));
var_dump($client->sendSms($req));
После того, как мы определили объекты, нам необходимо создать объект ($req
), который будем отправлять на сервер. После чего идут две самые заветные для нас строки! Наш SOAP-клиент! Верите или нет, но этого достаточно для того, чтобы на наш сервер начали сыпаться сообщения от клиента, а также для того, чтобы наш сервер успешно их принимал и обрабатывал! В первой из них мы создаем экземпляр класса SoapClient и передаем в его конструктор адрес расположения WSDL-файла, а в параметрах явно указываем, что работать мы будем по протоколу SOAP версии 1.2. В следующей строке мы вызываем метод sendSms
объекта $client
и сразу же выводим в браузере результат. Мне с сервера вернулся следующий объект: Object(stdClass)
public "status" => boolean true
Теперь посмотрим на лог, который мы предусмотрительно ведем на серверной стороне! В первой его части мы видим необработанные данные, которые поступили на сервер:
Object(stdClass)
public "messageList" =>
object(stdClass)
public "message" =>
object(stdClass)
public "phone" => string "79871234567" (length=11)
public "text" => string "Тестовое сообщение 1" (length=37)
public "date" => string "2013-07-21T15:00:00.26" (length=22)
public "type" => string "15" (length=2)
// создаем объект для отправки на сервер
$req = new Request();
$req->messageList = new MessageList();
$msg1 = new Message();
$msg1->phone = "79871234567";
$msg1->text = "Тестовое сообщение 1";
$msg1->date = "2013-07-21T15:00:00.26";
$msg1->type = 15;
$msg2 = new Message();
$msg2->phone = "79871234567";
$msg2->text = "Тестовое сообщение 2";
$msg2->date = "2014-08-22T16:01:10";
$msg2->type = 16;
$msg3 = new Message();
$msg3->phone = "79871234567";
$msg3->text = "Тестовое сообщение 3";
$msg3->date = "2014-08-22T16:01:10";
$msg3->type = 17;
$req->messageList->message = $msg1;
$req->messageList->message = $msg2;
$req->messageList->message = $msg3;
Object(stdClass)
public "messageList" =>
object(stdClass)
public "message" =>
object(stdClass)
public "Struct" =>
array (size=3)
0 =>
object(stdClass)
public "phone" => string "79871234567" (length=11)
public "text" => string "Тестовое сообщение 1" (length=37)
public "date" => string "2013-07-21T15:00:00.26" (length=22)
public "type" => string "15" (length=2)
1 =>
object(stdClass)
public "phone" => string "79871234567" (length=11)
public "text" => string "Тестовое сообщение 2" (length=37)
public "date" => string "2014-08-22T16:01:10" (length=19)
public "type" => string "16" (length=2)
2 =>
object(stdClass)
public "phone" => string "79871234567" (length=11)
public "text" => string "Тестовое сообщение 3" (length=37)
public "date" => string "2014-08-22T16:01:10" (length=19)
public "type" => string "17" (length=2)
$req->messageList->message = (object)$req->messageList->message;
Object(stdClass)
public "messageList" =>
object(stdClass)
public "message" =>
object(stdClass)
public "BOGUS" =>
array (size=3)
0 =>
object(stdClass)
public "phone" => string "79871234567" (length=11)
public "text" => string "Тестовое сообщение 1" (length=37)
public "date" => string "2013-07-21T15:00:00.26" (length=22)
public "type" => string "15" (length=2)
1 =>
object(stdClass)
public "phone" => string "79871234567" (length=11)
public "text" => string "Тестовое сообщение 2" (length=37)
public "date" => string "2014-08-22T16:01:10" (length=19)
public "type" => string "16" (length=2)
2 =>
object(stdClass)
public "phone" => string "79871234567" (length=11)
public "text" => string "Тестовое сообщение 3" (length=37)
public "date" => string "2014-08-22T16:01:10" (length=19)
public "type" => string "17" (length=2)
// создаем объект для отправки на сервер
$req = new Request();
$msg1 = new Message();
$msg1->phone = "79871234567";
$msg1->text = "Тестовое сообщение 1";
$msg1->date = "2013-07-21T15:00:00.26";
$msg1->type = 15;
$msg2 = new Message();
$msg2->phone = "79871234567";
$msg2->text = "Тестовое сообщение 2";
$msg2->date = "2014-08-22T16:01:10";
$msg2->type = 16;
$msg3 = new Message();
$msg3->phone = "79871234567";
$msg3->text = "Тестовое сообщение 3";
$msg3->date = "2014-08-22T16:01:10";
$msg3->type = 17;
$req->messageList = $msg1;
$req->messageList = $msg2;
$req->messageList = $msg3;
$req->messageList = (object)$req->messageList;
Object(stdClass)
public "messageList" =>
object(stdClass)
public "BOGUS" =>
array (size=3)
0 =>
object(stdClass)
public "phone" => string "79871234567" (length=11)
public "text" => string "Тестовое сообщение 1" (length=37)
public "date" => string "2013-07-21T15:00:00.26" (length=22)
public "type" => string "15" (length=2)
1 =>
object(stdClass)
public "phone" => string "79871234567" (length=11)
public "text" => string "Тестовое сообщение 2" (length=37)
public "date" => string "2014-08-22T16:01:10" (length=19)
public "type" => string "16" (length=2)
2 =>
object(stdClass)
public "phone" => string "79871234567" (length=11)
public "text" => string "Тестовое сообщение 3" (length=37)
public "date" => string "2014-08-22T16:01:10" (length=19)
public "type" => string "17" (length=2)
Так случилось, что в последнее время я стал заниматься разработкой веб-сервисов. Но сегодня топик не обо мне, а о том, как нам написать свой XML Web Service основанный на протоколе SOAP 1.2.
Как вы могли догадаться, вся магия будет твориться с использованием PHP и встроенных классов SoapClient и SoapServer. В качестве кролика у нас будет выступать сервис по отправке sms-сообщений.1 Постановка задачи
1.1 Границы
В начале предлагаю разобраться с тем результатом, которого мы достигнем в конце топика. Как было объявлено выше, мы будем писать сервис по отправке sms-сообщений, а если еще точнее, то к нам будут поступать сообщения из разных источников по протоколу SOAP. После чего, мы рассматрим в каком виде они приходят на сервер. Сам процесс постановки сообщений в очередь для их дальнейшей отправки провайдеру, к сожалению, выходит за рамки данного поста по многим причинам.1.2 Какими данными будем меняться?
Отлично, с границами мы определились! Следующий шаг, который необходимо сделать – решить какими данными мы будем обмениваться между сервером и клиентом. На эту тему предлагаю долго не мудрить и сразу для себя ответить на главные вопросы:
Что-то мне подсказывает, что для этого необходимо посылать следующее:
В принципе, двух этих характеристик достаточно для отправки, но мне сразу представляется случай, как sms-ка с поздравлением о дне рождения приходит вам в 3 часа утра, или 4! В этот момент я буду всем очень благодарен за то, что про меня не забыли! Поэтому, мы также будем посылать на сервер и
Следующее, что я бы хотел отправлять на сервер, так это
Данный параметр не является обязательным, но он может нам очень сильно пригодиться в случае, если быстро понадобится сказать боссу о том, скольких из наших клиентов мы «обрадовали» своим известием, а также нарисовать какую-нибудь красивую статистику на этот счет.2 С чем есть SOAP?
Вообще, изначально я не планировал ничего писать о том, что такое SOAP и хотел ограничиться ссылками на сайт w3.org с нужными спецификациями, а также ссылками на Wikipedia. Но в самом конце решил написать коротенькую справочку об этом протоколе.
Как видно, данный файл и есть весь веб-сервис. Указав в клиенте адрес WSDL-файла мы будем знать об любом веб-сервисе все! В результате, нам не надо абсолютно ничего знать о том, где расположен сам веб-сервис. Достаточно знать адрес расположения его WSDL-файла! Скоро мы узнаем, что не так страшен SOAP как его малюют (с) русская пословицы.3 Введение в XML-Schema
Теперь мы много чего знаем о то, что такое SOAP, что находится у него внутри и имеем обзорное представление о том, какой стек технологий его окружает. Поскольку, прежде всего SOAP представляет собой способ взаимодействия между клиентом и сервером, и в качестве транспорта для него используется язык разметки XML, то в данном разделе мы немного разберемся каким образом происходит автоматическая валидация данных посредством XML-схем.
Что-то очень простое, у чего внутри нет расширений. Их антиподом являются сложные комплексные типы. Самый простой пример комплексного типа, который приходит всем в голову – объекты. Например, книга. Книга состоит из свойств: автор
, название
, цена
, ISBN номер
и т.д. И эти свойства, в свою очередь, могут быть как простыми типами, так и комплексными. И задача XML-схемы это описать.
Схема нашего комплексного типа будет выглядеть следующим образом:
Эта запись читается следующим образом: у нас есть переменная «message
» типа «Message
» и есть комплексный тип с именем «Message
», который состоит из последовательного набора элементов «phone
» типа string
, «text
» типа string
, «date
» типа dateTime
, «type
» типа decimal
. Эти типы простые и уже определены в описании схемы. Поздравляю! Мы только что написали нашу первую XML-схему!
Схема для такого комплексного типа будет выглядеть так:
В первом блоке идет знакомое нам декларирование комплексного типа «Message
». Если вы заметили, то в каждом простом типе, входящем в «Message
», были добавлены новые уточняющие атрибуты «minOccurs
» и «maxOccurs
». Как не трудно догадаться из названия, первый (minOccurs
) сообщает о том, что в данной последовательности должно быть минимум по одному элементу типа «phone
», «text
», «date
» и «type
», в то время как следующий (maxOccurs
) атрибут нам декларирует, что таких элементов в нашей последовательности максимум по-одному. В результате, когда мы пишем свои схемы для каких-либо данных, нам предоставляется широчайший выбор по их настройке!4 Пишем свой WSDL
Вы помните о том, что WSDL и есть наш веб-сервис? Надеюсь, что помните! Как мы его напишем, так на нем наш маленький веб-сервис и поплывет. Поэтому, предлагаю не халтурить.
Но на практике, я обычно отправлял посредством PHP HTTP-заголовок«text/xml
»:
и все прекрасно работало!
Обычно, WSDL состоит из 4-5 основных блоков. Самый первый блок – определение веб-сервиса или другими словами – точки входа.
Здесь написано, что у нас есть сервис, который называется – «SmsService
». В принципе, все имена в WSDL-файле могут быть вами изменены на какие только пожелаете, т.к. они не играют абсолютно никакой роли.
Для этого перечисляется какие операции и в каком виде у будут вызываться. Т.е. для порта «SmsServicePort
» определена привязка под именем «SmsServiceBinding
», которая имеет тип вызова «rpc
» и в качестве протокола передачи (транспорта) используется HTTP. Т.о., мы здесь указали, что будем осуществлять RPC вызов поверх HTTP. После этого мы описываем какие процедуры (operation
) поддерживаются в веб-сервисе. Мы будем поддерживать всего одну процедуру – «sendSms
». Через эту процедуру будут отправляться на сервер наши замечательные сообщения! После того, как была объявлена процедура, необходимо указать в каком виде будут передаваться данные. В данном случае указано, что будут использоваться стандартные SOAP-конверты.
Для этого мы указываем, что наша привязка («binding») имеет тип «SmsServicePortType
» и в элементе «portType
» с одноименным типу именем указываем привязку процедур к сообщениям. И так, входящее сообщение (от клиента к серверу) будет называться «sendSmsRequest
», а исходящее (от сервера к клиенту) «sendSmsResponse
». Как и все имена в WSDL, имена входящих и исходящих сообщения – произвольные.
Для этого мы добавляем элементы «message
» с именами «sendSmsRequest
» и «sendSmsResponse
» соответственно. В них мы указываем, что на вход должен прийти конверт, структура которого соответствует типу данных «Request
». После чего с сервера возвращается конверт содержащий тип данных – «Response
».
Можно нас поздравить! Наш первый WSDL был написан! И мы еще на один шаг приблизились к достижению поставленной цели.
Далее мы разберемся с тем, что нам предоставляет PHP для разработки собственных распределенных приложений.5 Наш первый SOAP-сервер
Ранее я писал, что для создания SOAP-сервера на PHP мы будем использовать встроенный класс SoapServer. Для того, чтобы все дальнейшие действия происходили также как и у меня, вам понадобиться немного подкрутить свой PHP. Если быть еще точнее, то необходимо убедиться, что у вас установлено расширение «php-soap». Как его поставить на ваш веб-сервере лучше всего прочитать на официальном сайте PHP (см. список литературы).
То, что находится выше строчки с функцией «ini_set», надеюсь, что объяснять не надо. Т.к. там определяется какие HTTP-заголовки мы будем отправлять с сервера клиенту и настраивается окружение. В строчке с «ini_set» мы отключаем кеширование WSDL-файла для того, чтобы наши изменения в нем сразу же вступали в действие на клиенте.
На этом этапе получившийся сервер нас должен устроить полностью, т.к. поступающие к нему конверты мы можем логировать и потом спокойно анализировать приходящие данные. Для того, чтобы мы могли что-либо получать на сервер, нам необходим клиент. Поэтому давайте им и займемся!6 SOAP-клиент на подходе
Прежде всего нам надо создать файл, в котором будем писать клиент. Как обычно, мы его создадим в корне хоста и назовем «client.php
», а внутри напишем следующее:
Опишем наши объекты. Когда мы писали WSDL в нем для входящего на сервер конверта описывались три сущности: Request
, MessageList
и Message
. Соответственно классы Request
, MessageList
и Message
являются отражениями этих сущностей в нашем PHP-скрипте.
Давайте запусти и посмотрим что-же у нас наконец-то получилось!
И это замечательно, т.к. теперь мы точно знаем о том, что наш сервер работает и не просто работает, но еще и может возвращать на клиент какие-то значения!
Это и есть конверт. Теперь вы знаете как он выглядит! Но постоянно на него любоваться нам вряд ли будет интересно, поэтому давайте десереализуем объект из лог-файла и посмотрим все ли у нас хорошо:
Как видим, объект десериализовался правильно, с чем я нас всех хочу поздравить! Далее нас ждет что-то более интересно! А именно – мы будем отправлять клиентом на сервер не одно sms-сообщение, а целую пачку (если быть точнее, то целых три)!7 Отправляем сложные объекты
Давайте подумаем над тем, как же нам передать целую пачку сообщений на сервер в одном пакете? Наверно, самым простым способом будет организация массива внутри элемента messageList! Давайте это сделаем:
В наших логах числится, что пришел следующий пакет от клиента:
Что за ерунда, скажете вы? И будете правы в некотором смысле, т.к. только что мы узнали о том, что какой объект ушел от клиента, то абсолютно в том же виде он пришел к нам на сервер в виде конверта. Правда, sms-сообщения сериализовались в XML не так, как нам было необходимо – они должны были быть обернуты в элементы message
, а не в Struct
. Теперь посмотрим в каком виде приходит такой объект в метод sendSms
:
Что нам дает это знание? Только то, что выбранный нами путь не является верным и мы не получили ответа на вопрос – «Как нам на сервере получить правильную структуру данных?». Но я предлагаю не отчаиваться и попробовать привести наш массив к типу объект
:
В этом случае, нам придет уже другой конверт:
Пришедший в метод sendSms
объект имеет следующую структуру:
Как по мне, то «от перемены мест слагаемых – сумма не меняется» (с). Что BOGUS
, что Struct
– цель нами до сих пор не достигнута! А для ее достижения нам необходимо сделать так, чтобы вместо этих непонятных названий отображалось наше родное message
. Но как этого добиться, автору пока не известно. Поэтому единственное, что мы можем сделать – избавить от лишнего контейнера. Другими словами, мы сейчас сделаем так, чтобы вместо message
стал BOGUS
! Для этого изменим объект следующим образом:
Вдруг нам повезет и из схемы подтянется правильное название? Для этого посмотрим на пришедший конверт:
Да, чуда не произошло! BOGUS
– не победим! Пришедший в sendSms
объект в этом случае будет выглядеть следующим образом:
Как говорится – «Почти»! На этой (немного печальной) ноте предлагаю потихонечку закругляться и сделать некоторые для себя выводы.8 Заключение
Наконец-то мы добрались сюда! Давайте определимся с тем, что вы теперь умеете делать:
Также, мы сделали для себя некоторые открытия в ходе нашего небольшого исследования: