Пишем SOAP клиент-серверное приложение на PHP. Введение в SOAP

Что такое веб-сервис SOAP (Simple Object Access Protocol) и с чем его «едят»? В предыдущей статье было сказано, что web-сервисы представляют собой процесс взаимодействия приложений по протоколу HTTP, а SOAP определяет протокол реализации web-сервиса и является дальнейшим развитием XML-RPC (XML-вызов удалённых процедур). Если не вдаваться в сложности процесса взаимодействия клиент/серверных приложений, то можно сказать, что имеется некий сервер, который управляет информацией (добавляет, удаляет, обновляет, хранит) и выдает клиентам информацию по запросам в формате XML. Клиенты могут обращаться к такому серверу только по протоколу HTTP с оформлением запроса в формате XML. Т.е. клиент с сервером обмениваются только XML-информацией, и ни каких картинок, аудио, видео. Такой сервер может предоставлять информацию (веб-сервис) о погоде, об авиационном или железнодорожном расписании, о курсе валют и т.д.

Каждый web-сервис SOAP имеет описание в виде файла wsdl. WSDL (Web Services Description Language) - язык описания веб-сервисов и доступа к ним в формате XML.

В данной статье рассмотрим вопрос создания WEB-сервиса SOAP в IDE Eclipse с практической точки зрения. Т.е. создадим, запустим и протестируем веб-сервис SOAP. Как говорится, лучше один раз, чем ни разу.

Apache Axis

Для разработки WEB-сервиса были использованы Eclipse Luna (4.4.2), Apache Tomcat v8.5.4 и Apache Axis. Полагаю, что версии Eclipse и Tomcat не критичны для решения данной задачи. Можно использовать и другие версии.

Особо следует сказать об Apache Axis (A pache eX tensible I nteraction S ystem), который является фреймворком веб-сервиса с открытым исходным кодом. AXIS включает в себя контейнер для размещения и использования веб-сервисов на серверах приложений, утилиты для работы с WSDL-описаниями, классы для разработки веб-сервисов и их клиентов. В нашем примере AXIS будет использоваться для создания и тестирования WEB-сервиса и вызываться он будет из Eclipse.

Описание примера

Чтобы в Eclipse создать простейший WEB-сервис SOAP, а потом запустить его и протестировать, надо хорошо знать Eclipse, поскольку он всё сделает сам, включая создание файла wsdl. Поэтому кодировать придется очень мало, а в большей степени разбираться со скриншотами интерфейса IDE Eclipse.

Создадим WEB-сервис, который управляет персоналом и позволяет добавлять и удалять сотрудников, просматривать список сотрудников и выполнять поиск сотрудника по идентификатору. Начнем с проекта, тип которого в Eclipse должен быть "Dynamic Web Project". Определим наименование проекта как "SoapServer" :

В проекте создадим класс сотрудника Person, интерфейс описания методов PersonService и класс реализации методов PersonServiceImpl. На этом программирование заканчивается.

Листинг класса Person

Класс сотрудника включает идентификатор id и имя name (проще и не придумать). Также в классе определим методы get/set.

Package com.example; import java.io.Serializable; public class Person implements Serializable { private static final long serialVersionUID = 1L; private int id; private String name; public Person() {} public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }

Листинг интерфейса PersonService

Интерфейс определяет методы добавления нового сотрудника addPerson, удаление сотрудника deletePerson, получение сотрудника по идентификатору getPerson и чтение списка сотрудников getPersons.

Package com.example; public interface PersonService { public boolean addPerson (Person p); public boolean deletePerson(int id); public Person getPerson (int id); public Person getPersons (); }

Листинг класса PersonServiceImpl

Класс PersonServiceImpl реализует методы интерфейса.

Package com.example; import java.util.Map; import java.util.Set; import java.util.HashMap; public class PersonServiceImpl implements PersonService { private static Map persons; public PersonServiceImpl() { if (persons == null) persons = new HashMap(); } @Override public boolean addPerson(Person p) { if (persons.get(p.getId()) != null) return false; persons.put(p.getId(), p); return true; } @Override public boolean deletePerson(int id) { if (persons.get(id) == null) return false; persons.remove(id); return true; } @Override public Person getPerson(int id) { return persons.get(id); } @Override public Person getPersons() { Set ids = persons.keySet(); Person p = new Person; int i = 0; for(Integer id: ids){ p[i] = persons.get(id); i++; } return p; } }

Примечание : при каждом вызове сервиса (одного из его методов) создается объект PersonServiceImpl, в конструкторе которого выполняется проверка создания коллекции сотрудников. Поэтому коллекция сотрудников объявлена статической.

Cоздание WEB-сервиса SOAP

Для создания WEB-сервиса необходимо выделить реализацию интерфейса PersonServiceImpl и в контекстном меню, вызываемом правой клавишей мыши, выбрать "Web Service" (New/Other/Web Services/Web Service). Будет открыто окно мастера создания веб-сервиса, в котором представлена конфигурация создания веб-сервиса, включающая Tomcat, Apache Axis и наш проект SoapServer (вот где объявилась среда разработки в виде Tomcat и Apache Axis ).

Здесь можно ничего не делать, только проверьте "Service Implementation", чтобы был выбран соответствующий класс, установите галочки публикации и мониторинга веб-сервиса в нижней части формы ("Publish the Web service", "Monitor the Web service") и можно переходить к следующему шагу нажатием кнопки Next.

На этом шаге будет представлен список всех методов веб-сервиса (класса реализации интерфейса), описанных в файле PersonServiceImpl.wsdl:

Можно завершить процесс создания WEB-сервиса нажатием кнопки Finish. Но из любопытства все же заглянем в последнее окно и нажмем кнопку Next.


Здесь мы видим, что мастер завершил свою работу и предлагает стартовать Tomcat. Нажимаем на кнопку "Start server" и завершаем после этого работу с мастером. Теперь можно посмотреть, что сделал мастер разработки WEB-сервиса с нашим проектом.

Мы только создали два класса и интерфейс, а остальное создал мастер, включая поддиректорию с файлами WEB-INF/PersonServiceImplService, файл WEB-INF/server-config.wsdd и описание сервиса в виде PersonServiceImpl.wsdl в поддиректории WEB-INF/wsdl. Он же внес в дескриптор приложения web.xml изменения, связанные с web-сервисом.

В скриншоте не развернута директория lib, в которую мастер включил необходимые для веб-сервиса библиотеки jar:

  • axis.jar
  • commons-discovery-0.2.jar
  • commons-logging.jar
  • jaxrpc.jar
  • saaj.jar
  • wsdl4j.jar

Дескриптор приложения web.xml

В дескриптор приложения вносить изменений не требуется, если не считать удаление отсутствующих в проекте открываемых страниц по умолчанию (секция . Мастер создания веб-сервиса всю необходимую информацию вписал сам.

SoapServer Apache-Axis Servlet AxisServlet org.apache.axis.transport.http.AxisServlet AxisServlet /servlet/AxisServlet AxisServlet *.jws AxisServlet /services/* Axis Admin Servlet AdminServlet org.apache.axis.transport.http.AdminServlet 100 AdminServlet /servlet/AdminServlet

Старт WEB-сервиса SOAP

Для работы с веб-сервисом необходимо стартовать (если еще не стартовано) web-приложение SoapServer. Для этого, нажимая правой клавишей мыши на проекте, вызываем контекстное меню и выбираем "Run As/Run on Server". В открывшемся окне выбора сервера приложений выделяем Tomcat и нажимаем кнопку Finish.


В браузере будет открыта страница проекта по умолчанию, которой у нас нет. Браузер/вкладку с открывшейся с ошибкой страницей можно закрыть, она нам не пригодится. На вкладке серверов (скриншот ниже) можно увидеть, что сервер стартован и приложение SoapServer загружено в его контейнер.


Для старта приложения управления веб-сервисом Axis необходимо найти и нажать в панели инструментов кнопку, представленную на следующем скриншоте с подсказкой "Launch the Web Service Explorer".

В результате в браузере будет открыта страница Web Services Explorer , интерфейс которой представлен на следующем скриншоте. Окно приложения Web Services Explorer включает панель инструментов с кнопками (справа вверху) и три области: Navigator, Actions, Status. На строку URL не обращаем внимания, нам с ней не работать.


Первое, что нам необходимо, так это выбрать режим WSDL. Для этого в панели инструментов находим нужную нам кнопку и нажимаем. Корневым элементом в дереве Navigator"а отобразиться "WSDL Main" и в панели Actions будут представлены компоненты для работы с WSDL (скриншот сверху). Здесь можно подключиться к веб-сервису в Интернете. Но мы пойдем другим путём и выберем наш файл PersonServiceImpl.wsdl с описанием веб-сервиса из запущенного проекта SoapServer. Вводим в строке соответствующий адрес URI, начинающийся с file: и нажимаем кнопку "GO". Если всё правильно сделано и всё работает, то в Navigator"e будет отображена структура нашего WEB-сервиса, как это представлено на следующем скриншоте.

Переходим к тестированию методов WEB-сервиса. Выполним только добавление новых сотрудников и просмотрим список. Остальные методы Вы можете проверить самостоятельно.

Добавление сотрудника

Для добавления нового сотрудника выделяем соответствующий метод в Navigator"e; в панели Actions в секции Body будет представлен выбранный метод и параметры. Вводим идентификатор, русскоязычное имя "Всеволод" и нажимаем кнопку GO. Результат выполнения метода веб-сервиса можно увидеть в панели Status.

Чтобы просмотреть текст запроса и ответа веб-сервиса в панели Status нажимаем на link "Source". В результате в интерфейсе панели (скриншот ниже) будут открыты компонент с описанием запроса "SOAP Request Envelope" и компонент с описанием ответа "SOAP Response Envelope" в формате XML. Обратите внимание, что русскоязычное имя в запросе отображено в виде вопросительных знаков. После этого были добавлены еще два сотрудника Serg (id=2) и Olga (id=3).

Чтение списка сотрудников

А как будет представлен сотрудник в списке, имя которого имеет кириллицу? Выделяем в Navigator"e метод getPersons, нажимаем кнопку "GO" и смотрим результат запроса в панели Status в режиме Form (следующий скриншот). Всё нормально, имя представлено верно в кириллице. Т.е. не потребовалось «изголяться» с charset"ами для передачи и чтения текстовой информации не в ASCII-коде.

Но вот при просмотре запроса и ответа в панели Status в режиме Source видим, что кириллица отображается в виде вопросительных знаков.

Заключение

В статье был рассмотрен вопрос создания и тестирования WEB-сервиса в IDE Eclipse. Основную цель, которую я преследовал при изложении данной информации, показать, что такое WEB-сервис SOAP, как его можно практически создать и протестировать. Представленный материал должен позволить Вам взглянуть во «внутренности» WEB-сервиса SOAP и понять сущность данной технологии.

Создание клиента WEB-сервиса на примере рассмотрен . Второй пример SOAP клиента с авторизацией представлен .

Что такое SOAP?

SOAP расшифровывается как Simple Object Access Protocol (Простой Протокол Доступа к Объектам). Надеюсь по прочтении статьи вам останется только недоумевать: "Что за странное название?"

SOAP в теперешней его форме – это метод удаленного вызова (RPC, Remote procedure Call) по сети. (Да, он также используется для передачи документов в виде XML, но мы это пока опустим).

Давайте разбираться. Представьте, что у вас есть сервис, который возвращает биржевую котировку (stock quote) для заданного тикера (stock symbol). Он посылает данные на сайт Nasdaq и формирует на основе возвращенного HTML нужный результат. Дальше, чтобы позволить другим разработчикам использовать его внутри своих приложений, вы делаете из этого сервиса компонент, который через Интернет находит информацию о котировках. Работает он отлично, пока в один прекрасный день Nasdaq не меняет разметку своих страниц. Вам приходится пересмотреть всю логику работы компонента и разослать обновления всем разработчикам, использующим его. А им в свою очередь необходимо разослать обновления всем своим пользователям. Если это происходит на более-менее постоянной основе, вы можете нажить немало врагов среди коллег-разработчиков. А с программистами, как известно, шутки плохи. Вы же не хотите завтра доставать фотографию любимого кота из офисного шредера, правда?

Что же делать? Посмотрим... все, что вам нужно, это предоставить одну функцию, которая будет принимать на вход тикер (типа string) и возвращать биржевую котировку (типа float или double). Так не проще ли было бы просто позволить вашим разработчикам каким-то образом вызвать эту функцию через Интернет? Отлично! Тоже мне новость, есть же COM и Corba, и Java, которые этим занимаются уже годами... что правда – то правда, но эти методы не без изъяна. Удаленная настройка COM не тривиальна. Кроме того, нужно открыть столько портов в брандмауэре, что на системного администратора пива не напасешься. Да, и придется забыть о пользователях всех операционных систем кроме Windows. Но ведь позьзователи Linux тоже иногда интересуются биржей.

Хотя, похоже, что не все потеряно для пользователей Linux, если они используют DCOM, больше здесь: http://www.idevresource.com/com/library/res/articles/comonlinux.asp.

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

SOAP – это стандарт, который позволяет вам описать такой удаленный вызов и вид, в котором будет возвращаться результат. Таким образом вам нужно разместить вашу функцию в приложении, доступном по сети и получать вызовы в виде SOAP пакетов. После этого вы валидируете входные данные, запускаете вашу функцию и возвращаете результат в новом SOAP пакете. Весь процесс может работать через HTTP, так что вам не придется открывать кучу портов в брандмауэре. Правда просто?

О чем эта статья

Это первая из серии статей о SOAP, которые мы пишем в Agni Software. В этой статье я постараюсь дать вам представление о том, что такое SOAP и как написать приложение, общающееся с SOAP сервером.

Soap и XML

Если вам SOAP пока еще кажется простым, добавим XML. Теперь вместо имени функции и параметров мы получаем довольно сложный XML-конверт, как будто созданный для того, чтобы сбить вас с толку. Но не спешите пугаться. Дальше – больше, и вам нужно увидеть всю картину, чтобы оценить всю сложность SOAP.
Если вы не знаете, что такое XML, для начала прочтите мою статью об XML здесь: http://www.agnisoft.com/white_papers/xml_delphi.asp.

Все SOAP пакеты имеют XML формат. Что это значит? Посмотрим. Взгляните на эту функцию (Pascal):
function GetStockQuote(Symbol: string) : double; Выглядит отлично, но проблема в том, что это – Pascal. Какая польза от этого простого определения для Java-разработчика? Или для кого-то, кто работает с VB? Нам нужно что-то, что будет понятно всем, даже VB-программистам. Так дайте им XML, содержащий одну и ту же инфрмацию (параметры, значения биржевых котировок и т.д.). Вы создаете SOAP пакет, который по сути является вызовом вашей функции, обернутый в XML, чтобы любое приложения на любой платформе могло его понять. Теперь посмотрим, как выглядит наш SOAP вызов:
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">


IBM


Информативно, правда? SOAP упрощается на глазах. Ладно, шутки в сторону. Теперь я постараюсь объяснить вам, как разобраться в этом SOAP вызове.

Расшифровка тегов

Первый тег, который бросается в глаза – это . Этот тег – внешняя оболочка SOAP пакета, содержащая несколько объявлений пространств имен, которые нас особо не интересуют, но очень важны для любого языка программирования или парсера. Пространства имен определяются для того, чтобы последующие префиксы, такие как "SOAP-ENV:" или "xsd:" воспринимались парсером.

Следующий тег – . (Мы пропустили тег, не представленный здесь – . Его нет в этом конкретном примере, но если вы хотите почитать о нем больше, обратитесь к спецификации SOAP здесь: http://www.w3.org/TR/SOAP/). Тег собственно и содержит SOAP вызов.

Следующий тег в списке – . Имя тега, GetStockQuote и есть вызываемая функция. Согласно терминологии SOAP, это называется операцией. Таким образом GetStockQuote – операция, которая должна быть выполнена. ns1 – это пространство имен, указывающее на urn:xmethods-quotes в нашем случае.

Лирическое отступление на счет пространств имен: Пространство имен дает возможность квалифицировать XML тег. Нельзя, к примеру, иметь две переменные с одинаковым именем в одной процедуре, но если они в двух разных процедурах, проблем не возникает. Таким образом процедура – это пространство имен, так как все имена в ней уникальны. Точно так же XML теги имеют свою область видимости внутри пространств имен, так что имея пространство имен и имя тега, можно однозначно его идентифицировать. Мы определим пространство имен как URI, чтобы отличать наш NS1 от подражателей. В приведенном выше примере NS1 – это алиас, указывающий на urn:xmethods-quotes.

Обратите внимание также на атрибут encodingStyle – этот атрибут определяет каким образом сериализуется SOAP вызов.

Внутри тега содержатся параметры. В нашем простейшем случаи у нас есть только один параметр – тег . Обратите внимание на эту строку возле тега:
xsi:type="xsd:string"
Приблизительно так в XML и определяются типы. (Обратите внимание на то, как хитро я использовал слово "приблизительно", делая обобщение о технологии, которая может измениться как только статья будет опубликована). Что конкретно это означает: тип, определенный в пространстве имен xsi, который, как вы заметили, определен в теге – xsd:string. А это в свою очередь string, определенный в пространстве имен xsd, опять-таки, определенном ранее. (Уверен, юристы бы от этого всего просто млели).

Внутри тега указано "IBM". Это – значение параметра symbol функции GetStockQuote.

Ну и в конце, как порядочные люди, мы закрыли все теги.

Вот и разобрались с SOAP пакетом, определяющим вызов к SOAP серверу. А SOAP сервер с помощью XML парсеров, красной кнопки и космической станции "МИР" декодирует этот вызов и определяет, что вам нужна биржевая котировка. Он тут же находит нужную котировку и возвращает вам ее в таком виде:
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>


34.5


После разворачивания SOAP конверта, срывания ленточек и шуршания оберткой, мы узнаем, что цена акции IBM – 34.5.

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

Таким образом мы знаем, чего ожидает SOAP сервер и что он вернет. Так КАК же отправить эту информацию? Использовать можно любой транспорт. Самым освещенным является HTTP. Я не стану вдаваться в подробности HTTP, для тех, кто не знает – это то, что использует ваш браузер, чтобы общаться с сайтами, на которые вы заходите.

Нужный HTTP запрос будт выглядеть приблизительно так:
POST /StockQuote HTTP/1.1
Host: www.stockquoteserver.com

Content-Length: nnnn
SOAPAction: "Some-URI"

The soap request packet here... Единственное, что еще стоит отметить – это заголовок SOAPAction. Этот заголовок указывает на цель запроса и является обязательным. Каждый SOAP сервер может иметь неограниченное количество функций и может использовать заголовок SOAPAction чтобы определить какую функцию вызывают. Брандмауэры и мультиплексоры также могут фильтровать контент на основании этого заголовка.

SOAP ответ от HTTP сервера будет выглядеть следующим образом:
HTTP/1.1 200 OK
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn

Soap Response packet here... Почему HTTP? Во-первых, сетевым администраторам не придется открывать уйму отдельных портов для SOAP вызовов... веб-сервер может спокойно обрабатывать вызовы, т.к. 80-й порт обычно открыт для всех для приема входящих запросов. Еще одним преимуществом является расширяемость веб-серверов с помощью CGI, ISAPI и других нативных модулей. Эта расширяемость позволяет написать модуль, обрабатывающий SOAP запросы не задевая другого веб-контента.

Вот и все

Надеюсь, эта статья помогла пролить немного света на SOAP. Если вы еще здесь и хотите почитать больше на эту тему, посетите сайт авторов: http://www.agnisoft.com/soap

Валентин Колесов
разработчик красноярского отделения санкт-петербургской компании "Астрософт", сертифицированный специалист Microsoft (MCSD, MCSE, MCDBA)
[email protected]

Демонстрация работы SOAP на примере написания Web-сервера

Что такое SOAP

Широко распространенные в настоящее время технологии удаленного вызова методов (DCOM, CORBA/IIOP и RMI) довольно сложны в настройке и организации взаимодействия. Это влечет за собой трудности в эксплуатации и функционировании распределенных систем (проблемы безопасности, неудобства транспорта через брандмауэры и т. д.). Многие проблемы удалось решить благодаря созданию SOAP (Simple Object Access Protocol, простой протокол доступа к объектам), простого протокола, основанного на XML и служащего для обмена сообщениями в распределенных средах (WWW). Протокол предназначен для создания Web-сервисов и удаленного вызова методов. SOAP можно использовать в сочетании с разными транспортными протоколами, включая HTTP, SMTP и др.

Что такое Web-сервисы

Web-сервисами мы называем активный контент, реализующий некоторую функциональность, и данные, расположенные на Web-серверах и предоставляемые для использования внешним приложениям. Web-сервисы полностью независимы от языка и платформы реализации. Внешние приложения работают с сервисами посредством стандартных протоколов и форматов данных. Технология Web-сервисов является краеугольным камнем программной модели Microsoft .NET.

Для демонстрации возможностей SOAP в статье использована недавно вышедшая реализация SOAP Toolkit версии 2.0 производства Microsoft. Следует заметить, что текущая версия Toolkit заметно отличается от предыдущей (Microsoft SOAP Toolkit for Visual Studio 6.0) и от бета-версии SOAP Toolkit 2.0.

Объектная модель SOAP Toolkit предоставляет как низкоуровневые, так и высокоуровневые интерфейсы (SOAPClient, SOAPServer), скрывающие от программиста всю "внутреннюю кухню" - разбор и формирование пакетов, вызов методов и т. п. С помощью этих интерфейсов можно весьма элегантным образом использовать Web-сервисы в разрабатываемых приложениях. Объект SOAPClient выступает в роли посредника (proxy), предоставляющего интерфейс Web-сервиса и позволяющего работать с ним как с обычным COM-объектом (рис. 1).

  1. Клиентское приложение создает экземпляр объекта SOAPClient.
  2. SOAPClient читает файлы описания методов Web-сервиса (на языках WSDL и Web Services Meta Language, WSML). Эти файлы могут храниться и на стороне клиента.
  3. Клиентское приложение, используя возможности позднего связывания методов объекта SOAPClient, вызывает метод сервиса. SOAPClient формирует пакет запроса (SOAP Envelope) и отправляет его на сервер. Можно применить любой транспортный протокол, но, как правило, используется HTTP.
  4. Серверное приложение Listener (это может быть ISAPI-приложение или ASP-страница) принимает пакет, создает объект SOAPServer и передает ему пакет запроса. Помимо этого Listener обрабатывает HTTP-пакеты от клиента, отправляет клиенту пакеты с результатом работы сервиса, обрабатывает ошибки и использует функциональность SOAP-объектов.
  5. SOAPServer читает описание Web-сервиса, загружает описание и пакет запроса в деревья XML DOM.
  6. SOAPServer вызывает метод объекта или приложения, реализующего сервис.
  7. Результаты выполнения метода или описание ошибки конвертируются объектом SOAPServer в пакет ответа и отправляются клиенту.
  8. Объект SOAPClient проводит разбор принятого пакета и возвращает клиентскому приложению результаты работы сервиса или описание возникшей ошибки.

WSDL-файл - это документ в формате XML, описывающий методы, предоставляемые Web-сервисом, а также параметры методов, их типы, названия и местонахождение сервиса Listener. Мастер SOAP Toolkit автоматически генерирует этот документ, фрагмент которого приведен ниже:

Тег Envelope должен быть корневым элементом пакета. Элемент Header не обязателен, а Body должен присутствовать и быть прямым потомком элемента Envelope. В случае ошибки выполнения метода сервер формирует пакет, содержащий в теге Body элемент Fault, который содержит подробное описание ошибки.

Если вы пользуетесь высокоуровневыми интерфейсами SOAPClient, SOAPServer, то вам не придется вдаваться в тонкости формата пакета, но при желании можно воспользоваться низкоуровневыми интерфейсами или же вообще создать пакет "вручную", используя любой язык программирования.

Объектная модель SOAP Toolkit дает возможность работать с объектами низкоуровневого API:

  • SoapConnector - обеспечивает работу с транспортным протоколом для обмена SOAP-пакетами.
  • SoapConnectorFactory - обеспечивает метод создания коннектора для транспортного протокола, указанного в WSDL-файле (тег).
  • SoapReader - читает SOAP-сообщения и строит деревья XML DOM.·
  • SoapSerializer - содержит методы создания SOAP-сообщения.·
  • IsoapTypeMapper, SoapTypeMapperFactory - интерфейсы, позволяющие работать со сложными типами данных.

С помощью объектов высокоуровневого API можно передавать данные только простых типов (int, string, float и т. п.), но спецификация SOAP 1.1 допускает работу с более сложными типами данных, например с массивами, структурами, списками и их комбинациями. Для работы с такими типами приходится использовать интерфейсы IsoapTypeMapper и SoapTypeMapperFactory.

Пример

Чтобы продемонстрировать работу SOAP, напишем несложный Web-сервис, который будет уметь складывать и вычитать числа. Для работы серверного приложения потребуется пакет IIS 5 в среде Windows 2000 или IIS4 на Windows NT 4.0 Service Pack 6, а также установленный SOAP Toolkit 2.0.

Требования клиентского приложения - ОС Microsoft Windows 98/Me или Windows NT 4.0 Service Pack 6/2000 Service Pack 1, а также установленный SOAP Toolkit 2.0.

Создание сервера

Откройте в VB новый проект ActiveX DLL. Измените название класса на SOAPClass, а имя проекта - на SOAPProj.

В классе создайте следующие методы:

В следующем окне Мастера можно выбрать методы, которые войдут в Web-сервис. В данном случае выберите все методы. Затем укажите, где будет находиться Web-приложение (например, http://wsd010/soap/), задайте тип приложения Listener (ASP или ISAPI), выберите ASP, формат схемы (по умолчанию). Укажите путь, где будут находиться файлы описания Web-сервиса, и кодировку. После окончания работы Мастера в Web-каталоге появятся файлы ASP, WSDL и WSML - это Listener для ASP и описания сервиса (см. листинги 1-3).

Осталось только настроить права доступа к Web-приложению - желательно установить NT Challenge/Response authentication. На этом работа по созданию сервера закончена.

Создание клиента

Откройте в VB новый проект Standard EXE. В меню Project/References сделайте ссылку на библиотеку Microsoft SOAP type library. Создайте на форме кнопку, в обработчике нажатия кнопки напишите следующий код:

Dim SoapClient As New SoapClient SoapClient.mssoapinit "http://wsd010/soap/SOAPService.wsdl" MsgBox SoapClient.AddNumbers(4, 3) MsgBox SoapClient.SubtractNumbers(3, 2)

Не забудьте изменить адрес Web-сервера и путь к WSDL-файлу во второй строке на адрес и путь к вашему Web-сервису.

После создания объекта SOAPClient его надо инициализировать - указать путь к документу описания Web-сервиса. После инициализации у объекта появятся методы Web-сервиса. С ними можно работать как с обычным COM-объектом.

С помощью замечательной утилиты MsSoapT.exe (Trace Utility), входящей в Toolkit, можно просматривать в режиме реального времени пакеты, идущие от клиента к серверу и обратно. Для этого надо в WSDL-файле найти тег и вместо строки типа http://wsd010/soap/SOAPService.ASP написать http://wsd010:8080/soap/SOAPService.ASP, то есть указать порт 8080. После этого нужно запустить утилиту трассирования и принять настройки по умолчанию, а затем создать новый объект Formatted Trace. Теперь все SOAP-пакеты работы с Web-сервисом доступны для оперативного просмотра. Если трассировщик не загружен, то в нужно убрать указание порта 8080. На рис. 4 приведено содержимое пакета запроса на выполнение метода SubstractNumbers.

А пакет ответа выглядит следующим образом:

1

Так выглядит пакет сервера, пришедший в ответ на запрос некорректного формата:

SOAP-ENV:Server Connector - Bad request to the server. -2146823238 800a13ba

Дополнительная информация по теме

http://msdn.microsoft.com/webservices/ и http://msdn.microsoft.com/soap/ - последние новости о SOAP и Web-сервисах от Microsoft. Там же можно найти свежие версии ПО.

http://www.vbxml.com/soap/ - много полезной информации для разработчиков. Есть презентации и учебники по SOAP.

http://www.w3.org/TR/SOAP/ - спецификация SOAP от W3C.

http://www.w3.org/TR/wsdl - сведения о стандарте Web Services Definition Language (WSDL) 1.1/

http://microsoft.public.xml.soap - в этой конференции специалисты помогут решить сложные проблемы программирования SOAP.

Листинг 1. Код Listener для ASP

<%@ LANGUAGE=VBScript %> <% Option Explicit On Error Resume Next Response.ContentType = "text/xml" Dim SoapServer If Not Application("SoapServerInitialized") Then Application.Lock If Not Application("SoapServerInitialized") Then Dim WSDLFilePath Dim WSMLFilePath WSDLFilePath = Server.MapPath("SOAPService.wsdl") WSMLFilePath = Server.MapPath("SOAPService.wsml") Set SoapServer = Server.CreateObject("MSSOAP.SoapServer") If Err Then SendFault "Cannot create SoapServer object. " & Err.Description SoapServer.Init WSDLFilePath, WSMLFilePath If Err Then SendFault "SoapServer.Init failed. " & Err.Description Set Application("SOAPServiceServer") = SoapServer Application("SoapServerInitialized") = True End If Application.UnLock End If Set SoapServer = Application("SOAPServiceServer") SoapServer.SoapInvoke Request, Response, "" If Err Then SendFault "SoapServer.SoapInvoke failed. " & Err.Description Sub SendFault(ByVal LogMessage) Dim Serializer On Error Resume Next " "URI Query" logging must be enabled for AppendToLog to work Response.AppendToLog " SOAP ERROR: " & LogMessage Set Serializer = Server.CreateObject("MSSOAP.SoapSerializer") If Err Then Response.AppendToLog "Could not create SoapSerializer object. " & Err.Description Response.Status = "500 Internal Server Error" Else Serializer.Init Response If Err Then Response.AppendToLog "SoapSerializer.Init failed. " & Err.Description Response.Status = "500 Internal Server Error" Else Serializer.startEnvelope Serializer.startBody Serializer.startFault "Server", "The request could not be processed due to a problem in the server. Please contact the system administrator. " & LogMessage Serializer.endFault Serializer.endBody Serializer.endEnvelope If Err Then Response.AppendToLog "SoapSerializer failed. " & Err.Description Response.Status = "500 Internal Server Error" End If End If End If Response.End End Sub %>

Листинг 2. Код файла WSDL

Листинг 3. Код файла WSDL

Постараюсь коротко и своими словами. SOAP сейчас весьма популярный протокол web API сервисов. То есть способ взаимодействия Вашей информационной системы через web с другими информационными системами.

В самом общем виде SOAP это протокол обмена структурированными XML сообщениями в произвольной распределенной вычислительной среде. Отсюда следует, что мы должны куда-то передать XML и получить назад также XML. Для передачи нам нужен транспорт. Или протокол передачи данных. Например SMTP, FTP, HTTP, HTTPS. Чаще всего HTTP, но это не принципиально.

Залогом успеха нашего обмена является способность правильно создать и передать XML запрос и правильно принять и разобрать XML ответ. Для того чтобы сделать все правильно нужно соблюдать (извиняюсь за тавтологию) правила. Эти правила описываются файлом специального формата: WSDL. WSDL - это язык описания правил использования web сервисов в том числе и сервиса SOAP.

Встроенный в 1С объект платформы WS-ссылка позволяет нам загрузить эти правила и на их основе использовать web сервис с максимальным удобством. Поставщик сервиса разрабатывает правила и размещает их по доступному для клиентов адресу. Естественно, что изменения в работе сервиса должны в случае необходимости сопровождаться внесением изменений в WSDL инструкцию. Но это не наша (клиента) забота. Для нас важны правила. Мы можем не иметь малейшего представления о том, как работает сервис, но точно знаем, что если обратимся к нему по правилам, то получим правильный ответ. Это сильно упрощает жизнь и разработку. Не знаю как Вам, а мне это нравится.

Вызвав одной универсальной функцией нужный метод (предварительно создав и передав нужную ему структуру параметров) SOAP сервиса Вы уже можете видеть результат в виде дерева. Описание ответных данных как правило содержится в документации поставщика, но это не обязательно. В любом случае в качестве ответа Вы получите некую структуру данных в виде XML файла, которая в общем виде может быть приведена к типу данных 1С "ДеревоЗначений". Вот это-то дерево и будет у Вас при любом вызове любого метода. Вызовите в этот момент отладчик для просмотра или выведите дерево результата на форму и разберитесь с каких ветвей снять заветные "плоды"-результаты. И все:)

Вот как просто можно вызвать и получить результаты разных методов в абсолютно разных сервисах (во втором примере метод без параметров):

Но как и в жизни в методы SOAP тоже можно обозначить как процедуры и функции. Процедуру мне посчастливилось увидеть всего один раз. Да и то, потому что публичный сервис с информацией о странах, который я использовал в качестве примера №1 перестал работать (на скрине он еще показан) и пришлось свочно искать ему замену. Что же делать с процедурой, ведь она не вернет нам ответ и не из чего будет создать дерево и, следовательно, неоткуда сорвать результат?

Но (опять же как часто бывает в жизни) с процедурой все стало даже проще. Она возвращает результат в свои же параметры. Как на скрине ниже:

Так как ПолучитьДеревоРезультатовДляSOAP задумывалась как функция то я ее заставил вернуть результат. В данном случае, не деревом, а структурой, содержащей выходные парметры метода. Какие из параметров выходные Вы всегда можете просмотреть в их свойствах. Хотя можно и не смотреть. Структура в результате будет содержать все выходные параметры метода. В нашем примере это "image" и "encoding". Получить результат из структуры оказывается еще проще. Если с дерева нужно "доставать", то в структуре все уже готово к употреблению.

В последний год мне довелось трижды связывать через web API свою конфигурацию с поставщиками услуг и информации, для получения данных или заказа услуг (например услуги доставки товара или рассылки СМС). В последний раз имея готовый код примера и обработку для 1С от поставщика я отказался от них и выбрал вариантом решения универсальный SOAP сервис. Просто потому, что так оказалось с наработанными ранее функциями проще.

Для данной публикации специально нашел парочку публичных SOAP ресурсов и слепил использующую их конфигурацию. 95% времени заняла работа с формами вызова и вывода данных. Зато в демке Вы можете видеть курс валют от ЦБ РФ на любую дату и краткий набор сведений о любой стране мира скриншот web страницы для произвольного url.

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

hats 23 июля 2013 в 13:09

Пишем SOAP клиент-серверное приложение на PHP

  • PHP
  • Tutorial

Всем привет!
Так случилось, что в последнее время я стал заниматься разработкой веб-сервисов. Но сегодня топик не обо мне, а о том, как нам написать свой XML Web Service основанный на протоколе SOAP 1.2.

Я надеюсь, что после прочтения топика вы сможете самостоятельно:

  • написать свою собственную серверную реализацию веб-приложения;
  • написать свою собственную клиентскую реализацию веб-приложения;
  • написать свое собственное описание веб-сервиса (WSDL);
  • отправлять клиентом массивы однотипных данных на сервер.
Как вы могли догадаться, вся магия будет твориться с использованием PHP и встроенных классов SoapClient и SoapServer. В качестве кролика у нас будет выступать сервис по отправке sms-сообщений.

1 Постановка задачи

1.1 Границы

В начале предлагаю разобраться с тем результатом, которого мы достигнем в конце топика. Как было объявлено выше, мы будем писать сервис по отправке sms-сообщений, а если еще точнее, то к нам будут поступать сообщения из разных источников по протоколу SOAP. После чего, мы рассматрим в каком виде они приходят на сервер. Сам процесс постановки сообщений в очередь для их дальнейшей отправки провайдеру, к сожалению, выходит за рамки данного поста по многим причинам.

1.2 Какими данными будем меняться?

Отлично, с границами мы определились! Следующий шаг, который необходимо сделать – решить какими данными мы будем обмениваться между сервером и клиентом. На эту тему предлагаю долго не мудрить и сразу для себя ответить на главные вопросы:
  • Какой минимум данных надо посылать на сервер, чтобы отправить sms-сообщение абоненту?
  • Какой минимум данных надо посылать с сервера, чтобы удовлетворить потребности клиента?
Что-то мне подсказывает, что для этого необходимо посылать следующее:
  • номер мобильного телефона, а также
  • текст sms-сообщения.
В принципе, двух этих характеристик достаточно для отправки, но мне сразу представляется случай, как sms-ка с поздравлением о дне рождения приходит вам в 3 часа утра, или 4! В этот момент я буду всем очень благодарен за то, что про меня не забыли! Поэтому, мы также будем посылать на сервер и
  • дату отправки sms-сообщения.
Следующее, что я бы хотел отправлять на сервер, так это
  • Тип сообщения.
Данный параметр не является обязательным, но он может нам очень сильно пригодиться в случае, если быстро понадобится сказать боссу о том, скольких из наших клиентов мы «обрадовали» своим известием, а также нарисовать какую-нибудь красивую статистику на этот счет.

И все же, я что-то забыл! Если еще немного порефлексировать, то стоит отметить, что клиент за раз может отправить на сервер как одно sms-сообщение, так и некоторое их количество. Другими словами, в одном пакете данных может быть от одного до бесконечности сообщений.

В результате мы получаем, что для отправки sms-сообщения нам необходимы следующие данные:

  • номер мобильного телефона,
  • текст sms-сообщения,
  • время отправки sms-сообщения абоненту,
  • тип сообщения.

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

  • TRUE – пакет успешно дошел до сервера, прошел аутентификацию и встал в очередь для отправки sms-провайдеру
  • FALSE – во всех остальных случаях

На этом мы закончили описание постановки задачи! И наконец-то приступим к самому интересному – будем разбираться что за диковинный зверь этот SOAP!

2 С чем есть SOAP?

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

И начну я свое повествование с того, что данный протокол обмена данными относится к подмножеству протоколов основанных на так называемой парадигме 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» описывается следующая информация:

  • Какие пространства имен использовались,
  • Какие схемы данных использовались,
  • Какие типы сообщений веб-сервис ждет от клиентов,
  • Какие данные принадлежат каким процедурам веб-сервиса,
  • Какие процедуры содержит веб-сервис,
  • Каким образом клиент должен вызывать процедуры веб-сервиса,
  • На какой адрес должны отправляться вызовы клиента.
Как видно, данный файл и есть весь веб-сервис. Указав в клиенте адрес WSDL-файла мы будем знать об любом веб-сервисе все! В результате, нам не надо абсолютно ничего знать о том, где расположен сам веб-сервис. Достаточно знать адрес расположения его WSDL-файла! Скоро мы узнаем, что не так страшен SOAP как его малюют (с) русская пословицы.

3 Введение в XML-Schema

Теперь мы много чего знаем о то, что такое SOAP, что находится у него внутри и имеем обзорное представление о том, какой стек технологий его окружает. Поскольку, прежде всего SOAP представляет собой способ взаимодействия между клиентом и сервером, и в качестве транспорта для него используется язык разметки XML, то в данном разделе мы немного разберемся каким образом происходит автоматическая валидация данных посредством XML-схем.

Основная задачи схемы – описать структуру данных которые мы собираемся обрабатывать. Все данные в XML-схемах делятся на простые (скалярные) и коплексные (структуры) типы. К простым типам относятся такие типы как:

  • строка,
  • число,
  • булево значение,
  • дата.
Что-то очень простое, у чего внутри нет расширений. Их антиподом являются сложные комплексные типы. Самый простой пример комплексного типа, который приходит всем в голову – объекты. Например, книга. Книга состоит из свойств: автор , название , цена , ISBN номер и т.д. И эти свойства, в свою очередь, могут быть как простыми типами, так и комплексными. И задача XML-схемы это описать.

Предлагаю далеко не ходить и написать XML-схему для нашего sms-сообщения! Ниже представлено xml-описание sms-сообщения:

71239876543 Тестовое сообщение 2013-07-20T12:00:00 12
Схема нашего комплексного типа будет выглядеть следующим образом:


Эта запись читается следующим образом: у нас есть переменная «message » типа «Message » и есть комплексный тип с именем «Message », который состоит из последовательного набора элементов «phone » типа string , «text » типа string , «date » типа dateTime , «type » типа decimal . Эти типы простые и уже определены в описании схемы. Поздравляю! Мы только что написали нашу первую XML-схему!

Думаю, что значение элементов «element » и «complexType » вам стало все более-менее понятно, поэтому не будем на них больше заострять внимание и переключимся сразу же на элемент-композитор «sequence ». Когда мы используем элемент-композитор «sequence » мы сообщаем о том, что элементы включенные в него должны всегда располагаться в указанной в схеме последовательности, а также все из них являются обязательными. Но не стоит отчаиваться! В XML-схемах есть еще два элемента-композитора: «choice » и «all ». Композитор «choice » сообщает о том, что должен быть какой-то один из перечисленных в нем элементов, а композитор «all » – любая комбинация перечисленных элементов.

Как вы помните, то в первом разделе топика мы договорились о том, что в пакете может передаваться от одного до бесконечности sms-сообщений. Поэтому предлагаю разобраться как такие данные декларируются в XML-схеме. Общая структура пакета может выглядеть следующим образом:

71239876543 Тестовое сообщение 1 2013-07-20T12:00:00 12 71239876543 Тестовое сообщение N 2013-07-20T12:00:00 12
Схема для такого комплексного типа будет выглядеть так:


В первом блоке идет знакомое нам декларирование комплексного типа «Message ». Если вы заметили, то в каждом простом типе, входящем в «Message », были добавлены новые уточняющие атрибуты «minOccurs » и «maxOccurs ». Как не трудно догадаться из названия, первый (minOccurs ) сообщает о том, что в данной последовательности должно быть минимум по одному элементу типа «phone », «text », «date » и «type », в то время как следующий (maxOccurs ) атрибут нам декларирует, что таких элементов в нашей последовательности максимум по-одному. В результате, когда мы пишем свои схемы для каких-либо данных, нам предоставляется широчайший выбор по их настройке!

Второй блок схемы декларирует элемент «messageList » типа «MessageList ». Видно, что «MessageList » представляет собой комплексный тип, который включает минимум один элемент «message », но максимальное число таких элементов не ограничено!

4 Пишем свой WSDL

Вы помните о том, что WSDL и есть наш веб-сервис? Надеюсь, что помните! Как мы его напишем, так на нем наш маленький веб-сервис и поплывет. Поэтому, предлагаю не халтурить.

Вообще, для того, чтобы у нас все работало правильно нам надо передавать клиенту WSDL-файл с правильным MIME-типом. Для этого необходимо настроить ваш веб-сервер соответствующим образом, а именно – установить для файлов с расширением «*.wsdl» MIME-тип равный следующей строке:

Application/wsdl+xml
Но на практике, я обычно отправлял посредством PHP HTTP-заголовок«text/xml »:

Header("Content-Type: text/xml; charset=utf-8");
и все прекрасно работало!

Хочу сразу предупредить, наш простенький веб-сервис будет иметь довольно внушительное описание, поэтому не пугайтесь, т.к. большая часть текста является обязательной водой и написав ее один раз можно постоянно копировать от одного веб-сервиса к другому!

Поскольку WSDL – это XML, то в самой первой строке необходимо прямо об этом и написать. Корневой элемент файла всегда должен называться «definitions »:


Обычно, WSDL состоит из 4-5 основных блоков. Самый первый блок – определение веб-сервиса или другими словами – точки входа.


Здесь написано, что у нас есть сервис, который называется – «SmsService ». В принципе, все имена в WSDL-файле могут быть вами изменены на какие только пожелаете, т.к. они не играют абсолютно никакой роли.

После этого мы объявляем о том, что в нашем веб-сервисе «SmsService » есть точка входа («port»), которая называется «SmsServicePort ». Именно в эту точку входа и будут отправляться все запросы от клиентов к серверу. И указываем в элементе «address » ссылку на файл-обработчик, который будет принимать запросы.

После того, как мы определили веб-сервис и указали для него точку входа – необходимо привязать к нему поддерживаемые процедуры:


Для этого перечисляется какие операции и в каком виде у будут вызываться. Т.е. для порта «SmsServicePort » определена привязка под именем «SmsServiceBinding », которая имеет тип вызова «rpc » и в качестве протокола передачи (транспорта) используется HTTP. Т.о., мы здесь указали, что будем осуществлять RPC вызов поверх HTTP. После этого мы описываем какие процедуры (operation ) поддерживаются в веб-сервисе. Мы будем поддерживать всего одну процедуру – «sendSms ». Через эту процедуру будут отправляться на сервер наши замечательные сообщения! После того, как была объявлена процедура, необходимо указать в каком виде будут передаваться данные. В данном случае указано, что будут использоваться стандартные SOAP-конверты.

После этого нам необходимо привязать процедуру к сообщениям:


Для этого мы указываем, что наша привязка («binding») имеет тип «SmsServicePortType » и в элементе «portType » с одноименным типу именем указываем привязку процедур к сообщениям. И так, входящее сообщение (от клиента к серверу) будет называться «sendSmsRequest », а исходящее (от сервера к клиенту) «sendSmsResponse ». Как и все имена в WSDL, имена входящих и исходящих сообщения – произвольные.

Теперь нам необходимо описать сами сообщения, т.е. входящие и исходящие:


Для этого мы добавляем элементы «message » с именами «sendSmsRequest » и «sendSmsResponse » соответственно. В них мы указываем, что на вход должен прийти конверт, структура которого соответствует типу данных «Request ». После чего с сервера возвращается конверт содержащий тип данных – «Response ».

Теперь надо сделать самую малость – добавить описание данных типов в наш WSDL-файл! И как вы думаете, как описываются в WSDL входящие и исходящие данные? Думаю, что вы уже все давно поняли и сказали сами себе, что при помощи XML-схем! И вы будете абсолютно правы!


Можно нас поздравить! Наш первый WSDL был написан! И мы еще на один шаг приблизились к достижению поставленной цели.
Далее мы разберемся с тем, что нам предоставляет PHP для разработки собственных распределенных приложений.

5 Наш первый SOAP-сервер

Ранее я писал, что для создания SOAP-сервера на PHP мы будем использовать встроенный класс SoapServer. Для того, чтобы все дальнейшие действия происходили также как и у меня, вам понадобиться немного подкрутить свой PHP. Если быть еще точнее, то необходимо убедиться, что у вас установлено расширение «php-soap». Как его поставить на ваш веб-сервере лучше всего прочитать на официальном сайте PHP (см. список литературы).

После того, как все было установлено и настроено нам необходимо будет создать в корневой папке вашего хостинга файл «smsservice.php » со следующим содержанием:

setClass("SoapSmsGateWay"); //Запускаем сервер $server->handle();
То, что находится выше строчки с функцией «ini_set», надеюсь, что объяснять не надо. Т.к. там определяется какие HTTP-заголовки мы будем отправлять с сервера клиенту и настраивается окружение. В строчке с «ini_set» мы отключаем кеширование WSDL-файла для того, чтобы наши изменения в нем сразу же вступали в действие на клиенте.

Теперь мы подошли к серверу! Как видим, весь SOAP-сервер занимает всего лишь три строки! В первой строке мы создаем новый экземпляр объекта SoapServer и передаем ему в конструктор адрес нашего WSDL-описания веб-сервиса. Теперь мы знаем, что он будет располагаться в корне хостинга в файле с говорящим именем «smsservice.wsdl.php ». Во второй строке мы сообщаем SOAP-серверу какой класс необходимо дергать для того, чтобы обработать поступивший с клиента конверт и вернуть конверт с ответом. Как вы могли догадаться, именно в этом классе будет описан наш единственный метод sendSms . В третьей строке мы запускаем сервер! Все, наш сервер готов! С чем я нас всех и поздравляю!

Теперь нам необходимо создать WSDL-файл. Для этого можно либо просто скопировать его содержимое из предыдущего раздела, либо позволить себе вольности и немного его «шаблонизировать»:

"; ?> /" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" name="SmsWsdl" xmlns="http://schemas.xmlsoap.org/wsdl/"> /"> /smsservice.php" />
На этом этапе получившийся сервер нас должен устроить полностью, т.к. поступающие к нему конверты мы можем логировать и потом спокойно анализировать приходящие данные. Для того, чтобы мы могли что-либо получать на сервер, нам необходим клиент. Поэтому давайте им и займемся!

6 SOAP-клиент на подходе

Прежде всего нам надо создать файл, в котором будем писать клиент. Как обычно, мы его создадим в корне хоста и назовем «client.php », а внутри напишем следующее:

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));
Опишем наши объекты. Когда мы писали WSDL в нем для входящего на сервер конверта описывались три сущности: Request , MessageList и Message . Соответственно классы Request , MessageList и Message являются отражениями этих сущностей в нашем PHP-скрипте.

После того, как мы определили объекты, нам необходимо создать объект ($req ), который будем отправлять на сервер. После чего идут две самые заветные для нас строки! Наш SOAP-клиент! Верите или нет, но этого достаточно для того, чтобы на наш сервер начали сыпаться сообщения от клиента, а также для того, чтобы наш сервер успешно их принимал и обрабатывал! В первой из них мы создаем экземпляр класса SoapClient и передаем в его конструктор адрес расположения WSDL-файла, а в параметрах явно указываем, что работать мы будем по протоколу SOAP версии 1.2. В следующей строке мы вызываем метод sendSms объекта $client и сразу же выводим в браузере результат.
Давайте запусти и посмотрим что-же у нас наконец-то получилось!

Мне с сервера вернулся следующий объект:

Object(stdClass) public "status" => boolean true
И это замечательно, т.к. теперь мы точно знаем о том, что наш сервер работает и не просто работает, но еще и может возвращать на клиент какие-то значения!

Теперь посмотрим на лог, который мы предусмотрительно ведем на серверной стороне! В первой его части мы видим необработанные данные, которые поступили на сервер:

79871234567 Тестовое сообщение 1 2013-07-21T15:00:00.26 15
Это и есть конверт. Теперь вы знаете как он выглядит! Но постоянно на него любоваться нам вряд ли будет интересно, поэтому давайте десереализуем объект из лог-файла и посмотрим все ли у нас хорошо:

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)
Как видим, объект десериализовался правильно, с чем я нас всех хочу поздравить! Далее нас ждет что-то более интересно! А именно – мы будем отправлять клиентом на сервер не одно sms-сообщение, а целую пачку (если быть точнее, то целых три)!

7 Отправляем сложные объекты

Давайте подумаем над тем, как же нам передать целую пачку сообщений на сервер в одном пакете? Наверно, самым простым способом будет организация массива внутри элемента messageList! Давайте это сделаем:

// создаем объект для отправки на сервер $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;
В наших логах числится, что пришел следующий пакет от клиента:

79871234567 Тестовое сообщение 1 2013-07-21T15:00:00.26 15 79871234567 Тестовое сообщение 2 2014-08-22T16:01:10 16 79871234567 Тестовое сообщение 3 2014-08-22T16:01:10 17
Что за ерунда, скажете вы? И будете правы в некотором смысле, т.к. только что мы узнали о том, что какой объект ушел от клиента, то абсолютно в том же виде он пришел к нам на сервер в виде конверта. Правда, sms-сообщения сериализовались в XML не так, как нам было необходимо – они должны были быть обернуты в элементы message , а не в Struct . Теперь посмотрим в каком виде приходит такой объект в метод sendSms :

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;
В этом случае, нам придет уже другой конверт:

79871234567 Тестовое сообщение 1 2013-07-21T15:00:00.26 15 79871234567 Тестовое сообщение 2 2014-08-22T16:01:10 16 79871234567 Тестовое сообщение 3 2014-08-22T16:01:10 17
Пришедший в метод sendSms объект имеет следующую структуру:

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)
Как по мне, то «от перемены мест слагаемых – сумма не меняется» (с). Что BOGUS , что Struct – цель нами до сих пор не достигнута! А для ее достижения нам необходимо сделать так, чтобы вместо этих непонятных названий отображалось наше родное message . Но как этого добиться, автору пока не известно. Поэтому единственное, что мы можем сделать – избавить от лишнего контейнера. Другими словами, мы сейчас сделаем так, чтобы вместо message стал BOGUS ! Для этого изменим объект следующим образом:

// создаем объект для отправки на сервер $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;
Вдруг нам повезет и из схемы подтянется правильное название? Для этого посмотрим на пришедший конверт:

79871234567 Тестовое сообщение 1 2013-07-21T15:00:00.26 15 79871234567 Тестовое сообщение 2 2014-08-22T16:01:10 16 79871234567 Тестовое сообщение 3 2014-08-22T16:01:10 17
Да, чуда не произошло! BOGUS – не победим! Пришедший в sendSms объект в этом случае будет выглядеть следующим образом:

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)
Как говорится – «Почти»! На этой (немного печальной) ноте предлагаю потихонечку закругляться и сделать некоторые для себя выводы.

8 Заключение

Наконец-то мы добрались сюда! Давайте определимся с тем, что вы теперь умеете делать:
  • вам по силам написать необходимый для вашего веб-сервиса WSDL-файл;
  • вы без всяких проблем можете написать свой собственный клиент способный общаться с сервером по протоколу SOAP;
  • вы можете написать свой собственный сервер общающийся с окружающим миром по SOAP;
  • вы можете отправлять массивы однотипных объектов на сервер со своего клиента (с некоторыми ограничениями).
Также, мы сделали для себя некоторые открытия в ходе нашего небольшого исследования:
  • нативный класс SoapClient не умеет правильно сериализовывать однотипные структуры данных в XML;
  • при сериализации массива в XML он создает лишний элемент с именем Struct ;
  • при сериализации объекта в XML он создает лишний элемент с именем BOGUS ;
  • BOGUS меньшее зло чем Struct из-за того, что конверт получается компактнее (не добавляются лишние namespace"ы в XML заголовке конверта);
  • к сожалению, класс SoapServer автоматически не валидирует данные конверта нашей XML-схемой (возможно, и другие сервера этого не делают).