Что такое SOAP? Как насчет IBM SOAP4J

Что такое SOAP? Как насчет IBM SOAP4J

Brett McLaughlin Перевод Илья Чекменев

SOAP - это простой протокол доступа к объектам (Simple Object Access Protocol). Если вы никогда прежде о нем не слышали, то должно быть вы живете в какой-нибудь глуши, вдали от цивилизации. Он стал последним писком моды в web программировании, и неотъемлемой частью web сервисов, которые с таким фанатизмом используются в web разработках последнего поколения. Если вы слышали о.NET, детище Microsoft, или peer-to-peer "революции", то вы слышали о технологиях, которые основаны на использовании SOAP (даже если вы не знаете что это такое). Существует не одна, а две реализации SOAP, от Apache и от Microsoft, которой посвящены тысячи страниц на их сайте технической поддержки MSDN (http://msdn.microsoft.com/).

В этой статье я расскажу вам что такое SOAP и почему он является такой важной частью в процессе развития парадигмы web программирования. Это поможет вам опустить фундаментальные основы и сразу приступить непосредственно к работе с инструментарием SOAP. Затем я дам беглый обзор существующих SOAP проектов и углубляюсь в реализацию от Apache. Эта статья не претендует на воссоздание полной картины SOAP, в моей книге "Java & XML, 2-я редакция " восполнено множество пробелов. Ответы на многие из вопросов, возникших после прочтения этой статьи вы найдете в книге.

Вступление

Для начала нужно разобраться в том, что же такое SOAP. Вы можете прочитать полное (и весьма длинное) заключение W3C по адресу http://www.w3.org/TR/SOAP . Затем, разобравшись и отбросив всю шелуху, вы поймете что SOAP это всего лишь протокол. Это простой протокол (для его использования, нет необходимости в написании нового), основанный на идее, что в некоторый момент в распределенной архитектуре возникает необходимость обмена информацией. Кроме того, для систем, в которых существует вероятность перегрузок и затруднений при обработке процессов, этот протокол весьма выгоден тем, что он легковесен и требует минимального количества ресурсов. Наконец, он позволяет осуществлять все операции через HTTP, что дает возможность обойти стороной такие хитрые штуки как firewall и уберечься от прослушивания при помощи сокетов немыслимого числа портов. Главное чтобы вы осознали это, а все остальное - детали.

Разумеется, вы хотели бы знать эти детали, и я не обойду их вниманием. В спецификации SOAP существует три базовых компонента: конверт SOAP (SOAP envelope), набор правил шифровки и средства взаимодействия между запросом и ответом. Давайте думать о сообщении SOAP как об обычном письме. Вы еще помните эти древние штуки в конвертах с почтовой маркой и адресом, записанном на лицевой стороне? Такая аналогия поможет более наглядно представить себе концепцию SOAP как "конверт". Рисунок 12-1 изображает SOAP процессы в форме этой аналогии.

Рисунок 12-1. Процесс сообщений SOAP

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

Конверт

Конверт SOAP аналогичен конверту обычного письма. Он содержит информацию о письме, которое будет шифровано в основном разделе SOAP, включая данные о получателе и отправителе, а также информация о самом сообщении. Например, заголовок конверта SOAP может указывать на то, как должно обрабатываться сообщение. Прежде чем приложение начнет обработку сообщения, оно анализирует информацию о сообщении, включая информацию о том, сможет ли оно вообще обработать это сообщение. В отличие от ситуации со стандартными XML-RPC вызовами (припоминаете? XML-RPC сообщения, шифровка и прочее, все объединяется в единый XML фрагмент), с SOAP текущая обработка происходит для того, чтобы узнать что-то о сообщении. Типичное SOAP сообщение может также включать стиль шифровки, которая поможет получателю в обработке сообщения. Пример 12-1 демонстрирует конверт SOAP, который завершается указанием кодировки.

Пример 12-1: Конверт SOAP

Soapbox http://www-106.ibm.com/developerworks/library/x-soapbx1.html

Как вы видите, шифровка задана внутри конверта, что позволяет приложению определить (используя значение атрибута encodingStyle ), сможет ли оно прочитать входящее сообщение, расположенное в элементе Body . Убедитесь в правильности пространства имен (namespace) конверта SOAP, или серверы SOAP, которые получат ваше сообщение, выдадут сообщение об ошибке из-за несоответствия версий, и вы не сможете взаимодействовать с ними.

Шифровка

Второй важный элемент SOAP - это возможность шифровки пользовательских типов данных. В RPC (и XML-RPC) шифровка может выполняться лишь для заранее определенных типов данных, которые поддерживаются в скачанном вами XML-RPC инструментарии. Шифровка других типов данных требует от вас самостоятельной модификации RPC сервера и клиента. С SOAP схема XML может быть довольно легко использована для указания новых типов данных (при помощи структуры complexType , рассмотренной во 2-й главе моей книги), и эти новые типы могут быть представлены в XML как часть основного раздела SOAP. Благодаря интеграции со схемой XML, вы можете шифровать любой тип данных в SOAP сообщении, логически описав его в схеме XML.

Вызов

Лучший способ понять как работает вызов SOAP - это сравнить его с чем-нибудь, что вам знакомо, например с XML-RPC. Если вы помните, XML-RPC вызов выглядит подобно фрагменту кода, представленному в Примере 12-2 .

Пример 12-2. Вызов в XML-RPC

// Указание используемого обработчика (парсера) XML XmlRpc.setDriver("org.apache.xerces.parsers.SAXParser"); // Указание сервера, к которому выполняется подключение XmlRpcClient client = new XmlRpcClient("http://rpc.middleearth.com"); // Создание параметров Vector params = new Vector(); params.addElement(flightNumber); params.addElement(numSeats); params.addElement(creditCardType); params.addElement(creditCardNum); // Запрос Boolean boughtTickets = (Boolean)client.execute("ticketCounter.buyTickets", params); // Обработка ответа

Я создал простейшую программу для заказа авиабилетов. Теперь взгляните на Пример 12-3 , который демонстрирует вызов в SOAP.

Пример 12-3. Вызов в SOAP

// Создание параметров Vector params = new Vector(); params.addElement(new Parameter("flightNumber", Integer.class, flightNumber, null)); params.addElement(new Parameter("numSeats", Integer.class, numSeats, null)); params.addElement(new Parameter("creditCardType", String.class, creditCardType, null)); params.addElement(new Parameter("creditCardNumber", Long.class, creditCardNum, null)); // Создание объекта Call Call call = new Call(); call.setTargetObjectURI("urn:xmltoday-airline-tickets"); call.setMethodName("buyTickets"); call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC); call.setParams(params); // Вызов Response res = call.invoke(new URL("http://rpc.middleearth.com"), ""); // Обработка ответа

Как вы видите, собственно вызов, представленный объектом Call , резидентен в памяти. Он позволяет вам задать цель вызова, метод вызова, стиль шифровки, параметры, и многие другие параметры, не представленное в этом примере. Это более гибкий механизм, чем метод XML-RPC, позволяющий вам явно задавать набор различных параметров, которые косвенно определяются в XML-RPC. Далее в этой статье вы узнаете больше о процессе вызова, в том числе вы узнаете как SOAP обрабатывает неверные запросы, иерархию ошибок и, разумеется, возвращаемые результаты вызова.

После такого краткого введения вы уже знаете достаточно, чтобы заинтересоваться этой забавной штукой. А теперь позвольте мне представить вам реализацию SOAP, которую я собираюсь использовать. Я объясню причины по которым остановил свой выбор именно на ней и рассмотрю некоторые примеры кода.

Настройка

Теперь, когда вы познакомились с основами концепции, настало время для самой интересной части: программирования. Для этого вам потребуется удобный проект или продукт, найти который проще чем может показаться на первый взгляд. Если вам нужен Java проект, предоставляющий возможности SOAP, то его не надо долго искать. Существует две группы продуктов: коммерческие и бесплатные. Как и в своей книге, я буду избегать упоминания коммерческих продуктов. Это вовсе не потому что они плохи (даже наоборот, некоторые из них прекрасны), а потому что я хотел бы чтобы любой читатель мог попробовать любой из приведенных примеров. Это связано с доступностью, которой многие коммерческие продукты не обладают. Вы должны заплатить за их использование, или временно использовать их в течении ограниченного периода времени после скачивания.

Таким образом мы плавно подошли к проектам с открытым источником (open source). Из этой области я могу назвать лишь один продукт: Apache SOAP. Он расположен по адресу http://xml.apache.org/soap и предоставляет инструментарий SOAP для Java. На момент написания книги вышла версия 2.2, которую вы можете скачать с web сайта Apache. Именно эту версию я и буду использовать в примерах для этой статьи.

Другие альтернативы

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

Как насчет IBM SOAP4J?

Первым в списке альтернатив значится реализация от IBM: SOAP4J. Работа IBM легла в основу проекта Apache SOAP, также как IBM XML4J переросла в то, что теперь известно как проект XML парсера Apache Xerces. Предполагается что реализация от IBM будет переработана, объединившись с Apache SOAP. Примерно то же случилось с IBM"овским XML4J: теперь он лишь обеспечивает пакетирование в Xerces. Это только лишь подчеркивает тенденции - крупные производители зачастую поддерживают и используют OpenSource проекты, в данном случае оба проекта (Apache и IBM) используют одну и ту же кодовую базу.

Разве Microsoft вне игры?

Разумеется нет. Microsoft и его реализация SOAP, равно как и все направление.NET (более подробно рассмотренное в моей книге), весьма значительны. В действительности я хотел уделить большую часть времени детальному рассмотрению реализации SOAP от Microsoft, но она поддерживает только COM объекты иже с ними и не поддерживает Java. Ввиду этих причин подобное описание не могло войти в статью про Java и XML. Тем не менее, Microsoft (несмотря на все претензии, которые мы, как разработчики, имеем к этой компании) проделала важную работу в сфере web сервисов, и вы совершите ошибку если без раздумий ее отвергнете, руководствуясь лишь голыми эмоциями. Если у вас есть необходимость работать с COM или компонентами Visual Basic, я настоятельно рекомендую вам попробовать использовать инструментарий Microsoft SOAP, доступный по адресу http://msdn.microsoft.com/library/default.asp?url=/nhp/Default.asp?contentid=28000523 наряду с множеством других ресурсов SOAP.

Что такое Axis?

Те из вас, кто следит за деятельностью Apache, должно быть слышали об Apache Axis. Axis это инструментарий SOAP следующего поколения, разрабатываемый также под эгидой Apache XML. За SOAP (спецификацией, а не специфической реализацией), стремительно и радикально развивающейся в последнее время, следить весьма трудно. Попытаться создать версию SOAP, полностью соответствующую текущим требованиям, изменяющимся в ходе развития, также довольно сложно. В результате, текущая версия Apache SOAP предлагает некое решение, ограниченное его конструкцией. Решив что не стоит пытаться полностью перепроектировать существующий инструмент, разработчики Apache приступили к созданию проекта на базе нового кода. Так родился Axis. Название SOAP также претерпело изменение, сначала из SOAP превратившись в XP, а затем в XMLP. Затем из имени нового SOAP было исключено название спецификации и родилось название "Axis". Но сейчас похоже что W3C вновь возвращается к названию спецификации SOAP (версии 1.2 или 2.0), так что все еще может поменяться и путаницы будет еще больше!

Думайте об IBM SOAP4J как об архитектуре?1 инструментария SOAP. А об Apache SOAP (рассматриваемой в этой статье), как об архитектуре?2. А Axis представляет собой архитектуру?3, архитектуру нового поколения. Этот проект использует SAX, тогда как Apache SOAP базируется на DOM. Кроме того, Axis, в отличие от Apache SOAP, предоставляет более дружественный подход при взаимодействии с пользователем. После перечисления этих достоинств вы наверное придете в недоумение, почему я не выбрал в качестве предмета изучения Axis. Просто это было бы несколько преждевременно. В настоящее время готовится к выходу лишь версия 0.51 Axis. Это еще не бета, и даже не альфа версия. Я с удовольствием мог бы расписывать новые возможности Axis, но у вас не было бы никаких шансов убедить ваше руководство в возможности использования программного обеспечения с открытым источником версии ниже альфы для нужд вашей сверх важной системы. Поэтому я решил сфокусироваться на чем-то, что вы реально можете использовать уже сегодня - Apache SOAP. Я думаю что к моменту выхода финальной версии Apache Axis, я обновлю этот материал в следующем издании своей книги. А до тех пор давайте сосредоточимся на решении, которое уже доступно.

Установка

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

Клиент

Для использования клиента SOAP вам необходимо сначала скачать Apache SOAP, доступный на http://xml.apache.org/dist/soap . Я скачал версию 2.2 в бинарном формате (из подкаталога version-2.2 ). Затем вы должны разархивировать содержимое архива в директорию на вашем компьютере. В моем случае это была директория javaxml2 (c:\javaxml2 на моем компьютере с Windows, /javaxml2 на моем компьютере с Mac OS X). В результате файлы разархивировались в /javaxml2/soap-2_2 . Вам также потребуется скачать пакет JavaMail, доступный на сервере Sun http://java.sun.com/products/javamail/ . Он потребуется для поддержки протокола передачи SMTP, используемого в Apache SOAP. Затем скачайте Java Beans Activation Framework (JAF), также доступный на сервере Sun http://java.sun.com/products/beans/glasgow/jaf.html . Исходя из предположения что у вас уже установлен и готов к использованию Xerces или другой XML парсер.

Примечание: Убедитесь в том, что ваш XML парсер JAXP-совместимый и корректно использует пространство имен. Скорее всего ваш парсер удовлетворяет этим требованиям.Если у вас возникли проблемы, лучше вернуться к использованию Xerces.

Примечание: Используйте последние версии Xerces. Версия 1.4 и выше подойдет. Существует ряд ошибок при работе с SOAP и Xerces 1.3(.1), поэтому я советую не использовать такое сочетание.

Разархивируйте JavaMail и JAF пакеты, а затем включите их jar файлы в ваш classpath, а также библиотеку soap.jar . Каждый из этих jar файлов должен находиться либо в корневом каталоге соответствующей программы, или в подкаталоге /lib . По завершении ваша переменная classpath должна выглядеть примерно следующим образом:

$ echo $CLASSPATH /javaxml2/soap-2_2/lib/soap.jar:/javaxml2/lib/xerces.jar: /javaxml2/javamail-1.2/mail.jar:/javaxml2/jaf-1.0.1/activation.jar

Для Windows она будет выглядеть так:

c:\>echo %CLASSPATH% c:\javaxml2\soap-2_2\lib\soap.jar;c:\javaxml2\lib\xerces.jar; c:\javaxml2\javamail-1.2\mail.jar;c:\javaxml2\jaf-1.0.1\activation.jar

И, наконец, добавьте директорию javaxml2/soap-2_2/ в ваш classpath для запуска примеров SOAP. Я описал настройку для нескольких примеров, приведенных в этой главе.

Сервер

Чтобы создать SOAP-совместимый набор серверных компонент вам для начала потребуется движок сервлета. Как и в предыдущих главах, в качестве примера для этой главы я использовал Apache Tomcat (доступный на http://jakarta.apache.org/). Вам потребуется добавить все необходимое клиенту в classpath сервера. Самый простой способ сделать это - сбросить soap.jar , activation.jar и mail.jar , а также ваш парсер, в каталог библиотек вашего движка сервлетов. Для Tomcat это каталог /lib, в котором содержатся библиотеки для автоматической загрузки. Если вы хотите обеспечить поддержку скриптов (которые не обсуждаются в этой главе, но есть в примерах Apache SOAP), вам нужно поместить bsf.jar (доступный на http://oss.software.ibm.com/developerworks/projects/bsf) и js.jar (доступный на http://www.mozilla.org/rhino/) в тот же каталог.

Примечание: Если вы используете Xerces c Tomcat, вам нужно будет повторить фокус, который я описывал в главе 10. Переименуйте parser.jar в z_parser.jar , а jaxp.jar в z_jaxp.jar , чтобы убедиться в том, что xerces.jar и включенная версия JAXP загружается перед каким-либо другим парсером или реализацией JAXP.

Затем перезагрузите ваш движок сервлетов, после чего вы будете готовы к написанию серверных компонент SOAP.

Сервлет маршрутизатора и клиент администратора

Помимо основных операций, Apache SOAP включает сервлет маршрутизатора, а также клиент администратора. Даже если вы не собираетесь их использовать, я рекомендую вам установить их, чтобы протестировать правильность установки SOAP. Этот процесс зависит от того, какой движок сервлетов вы используете, поэтому я ограничусь описанием процесса установки для Tomcat. Инструкции по установке для некоторых других движков сервлетов вы найдете на http://xml.apache.org/soap/docs/index.html .

Установка под Tomcat очень проста: просто возьмите файл soap.war из директории soap-2_2/webapps и сбросьте его в директорию $TOMCAT_HOME/webapps - и все! Чтобы проверить установку укажите в броузере адрес http://localhost:8080/soap/servlet/rpcrouter . Вы должны получить ответ, наподобие указанного на рис.12-2 .

Рисунок 12-2. Сервлет RPC маршрутизатора

Несмотря на то что сообщение похоже на сообщение об ошибке, оно свидетельствует о том что все работает правильно. Вы должны получить такой же отклик, указав в броузере адрес клиента администратора: http://localhost:8080/soap/servlet/messagerouter .

В завершение тестирования сервера и клиента, убедитесь в том, что вы полностью следовали всем инструкциям. Затем запустите следующий класс Java, как это показано ниже, для поддержки URL вашего сервлета для сервлета маршрутизатора RPC:

C:\>java org.apache.soap.server.ServiceManagerClient http://localhost:8080/soap/servlet/rpcrouter list Deployed Services:

Вы должны получить пустой список сервисов, как это показано выше. Если вы получите какие-либо сообщения, ознакомьтесь с длинным перечнем возможных ошибок, доступным на http://xml.apache.org/soap/docs/trouble/index.html . Это наиболее полный перечень проблем, с которыми вы могли столкнуться. Если вы получили пустой список, это значит что настройка завершена и вы готовы начать рассмотрение примеров, приведенных в этой главе.

Приступим

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

  • Выбор между SOAP-RPC и SOAP сообщениями;
  • Написание или получение доступа к SOAP сервису;
  • Написание или получение доступа к SOAP клиенту.

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

RPC или Messaging?

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

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

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

RPC сервис

Теперь, когда с формальностями покончено, настало время действовать. Как вы знаете, в RPC вам потребуются классы, методы которых будут выполняться удаленно.

Фрагменты кода

Я начну с рассмотрения фрагментов кода для сервера. Эти фрагменты представляют собой классы с методами, выполняемыми для RPC клиентов . В качестве примеров я использовал код из своей книги. Вместо того, чтобы использовать простые классы, я выбрал более сложный пример чтобы как можно наглядней продемонстрировать возможности SOAP. Итак, в качестве примера я использовал класс CD. Сначала определяем элемент map для каждого нестандартного типа параметров. Для атрибута encodingStyle , по крайней мере в Apache SOAP 2.2. вы должны указать значение http://schemas.xmlsoap.org/soap/encoding/ . На данный момент это единственная поддерживаемая кодировка. Вам нужно указать пространство имен для типа, определяемого пользователем, а затем имя класса с префиксом пространства имен для этого типа. В нашем случае для этих целей я использовал выдуманное пространство имен и простой префикс "x ". Затем, используя атрибут javaType , задал реальное имя Java класса (для этого случая - javaxml2.CD ). И, наконец, куралесил с атрибутами java2XMLClassName и xml2JavaClassName . С их помощью задается класс, конвертируемый из Java в XML и наоборот. Я использовал потрясающе удобный класс BeanSerializer, также входящий в комплект поставки Apache SOAP. Если ваш пользовательский параметр в формате JavaBean, этот сериализатор и десериализатор избавит вас от необходимости писать свой собственный. Вам нужен класс с конструктором по умолчанию (вспомните, для класса CD я задал простой, без каких-либо параметров, конструктор), и публикация всех данных этого класса при помощи методов setXXX и getXXX . Поскольку класс CD прекрасно удовлетворяет всем этим требованиям, BeanSerializer работает идеально.

Примечание: То, что класс CD соответствует требованиям BeanSerializer . не имеет большого значения. Большинство классов легко приводятся к этому формату. Поэтому я советую избегать написания собственных сериализаторов и десериализаторов. Это лишняя головная боль (ничего сложного, но слишком кропотливо) и рекомендую вам сэкономить силы и использовать в своих пользовательских параметрах конвертацию бинов. Во многих случаях преобразования бинов требуют лишь наличия в вашем классе конструктора по умолчанию (без параметров).

Теперь повторно воссоздадим jar файл и переразместим наш сервис:

(gandalf)/javaxml2/Ch12$ java org.apache.soap.server.ServiceManagerClient http://localhost:8080/soap/servlet/rpcrouter xml/CDCatalogDD.xml

Внимание: Если вы оставите запущенным свой движок сервлета и в это же время переразмещаете сервис, вам потребуется перезапустить движок сервлета, чтобы активировать новые классы для сервиса SOAP и переразместить сервис.

Теперь осталось модифицировать клиент для использования новых классов и методов. Пример 12-10 содержит модифицированную версию клиентского класса CDAdder . Изменения, внесенные в предыдущую версию, выделены.

Пример 12-10: Обновленный класс CDAdder

package javaxml2; import java.net.URL; import java.util.Vector; import org.apache.soap.Constants; import org.apache.soap.Fault; import org.apache.soap.SOAPException; import org.apache.soap.encoding.SOAPMappingRegistry; import org.apache.soap.encoding.soapenc.BeanSerializer; import org.apache.soap.rpc.Call; import org.apache.soap.rpc.Parameter; import org.apache.soap.rpc.Response; import org.apache.soap.util.xml.QName; public class CDAdder { public void add(URL url, String title, String artist, String label) throws SOAPException { System.out.println("Добавление CD с названием "" + title + "" исполнителя "" + artist + "" студии " + label); CD cd = new CD(title, artist, label); // Создание объекта вызова Call Call call = new Call(); call.setSOAPMappingRegistry(registry); call.setTargetObjectURI("urn:cd-catalog"); call.setMethodName("addCD"); call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC); // Установка параметров Vector params = new Vector(); params.addElement(new Parameter("cd", CD.class, cd, null)); call.setParams(params); // Обработка Invoke вызова Response response; response = call.invoke(url, ""); if (!response.generatedFault()) { System.out.println("Добавление CD успешно завершено."); } else { Fault fault = response.getFault(); System.out.println(Ошибка: " + fault.getFaultString()); } } public static void main(String args) { if (args.length != 4) { System.out.println("Шаблон: java javaxml2.CDAdder " + "\"[Заголовок CD]\" \"[Имя исполнителя]\" \"[Студия CD]\""); return; } try { // URL SOAP сервера, к которому осуществляется подключение URL url = new URL(args); // Get values for new CD String title = args; String artist = args; String label = args; // Add the CD CDAdder adder = new CDAdder(); adder.add(url, title, artist, label); } catch (Exception e) { e.printStackTrace(); } } }

Единственное по-настоящему интересное изменение связано с отображением класса CD :

// Отображение этого типа, чтобы его можно было использовать с SOAP SOAPMappingRegistry registry = new SOAPMappingRegistry(); BeanSerializer serializer = new BeanSerializer(); registry.mapTypes(Constants.NS_URI_SOAP_ENC, new QName("urn:cd-catalog-demo", "cd"), CD.class, serializer, serializer);

Вот каким образом пользовательский параметр может быть кодирован и передан по сети. Я уже рассказывал каким образом класс BeanSerializer может быть использован для обработки параметров в формате JavaBean, например таких как класс CD . Для их указания серверу я использовал дискриптор размещения, несмотря на это теперь мне нужно сообщить клиенту, что нужно использовать этот сериализатор и десериализатор. Эту функцию и выполняет класс SOAPMappingRegistry . Метод mapTypes() берет зашифрованную строку (и вновь для этого лучше использовать константу NS_URI_SOAP_ENC ), и информацию о типе параметра, для которого должна использоваться специальная сериализация. Сначала указывается QName. Вот почему в дискрипторе размещения было использовано странное пространство имен. Вам нужно указать здесь тот же URN, а также локальное имя элемента (для этого примера "CD"), затем Java объект Class класса, который будет сериализован (CD.class ) и, наконец, экземпляр класса для сериализации и десериализации. Для этого примера в обоих случаях будет фигурировать экземпляр BeanSerializer . После того как все эти настройки будут внесены в реестр, сообщите об этом объекту Call при помощи метода setSOAPMapping-Registry() .

Вы можете запустить этот класс, как было показано раньше, добавив CD, и все должно работать как положено:

C:\javaxml2\build>java javaxml2.CDAdder http://localhost:8080/soap/servlet/rpcrouter "Tony Rice" "Manzanita" "Sugar Hill" Добавление CD с заголовком "Tony Rice" исполнителя "Manzanita" студии Sugar Hill Успешное добавление CD.

Я оставил модификацию класса CDLister для вас. Все производится по тому же шаблону. Чтобы проверить себя можете обратиться к файлам с примерами к моей книге, которые уже содержат эти обновленные классы.

Примечание: Вы можете решить что, поскольку класс CDLister напрямую не взаимодействует с объектом CD (возвращаемое методом list() значение имеет тип Hashtable ), то вам не нужно вносить каких-либо изменений. Тем не менее, возвращаемый класс Hashtable содержит экземпляры объекта CD . Если SOAP не знает как десериализовать их, клиент выдаст сообщение об ошибке. В этом случае для решения проблемы вы должны указать в объекте Call экземпляр SOAPMappingRegistry .

Эффективная обработка ошибок

Теперь, когда вы познакомились с пользовательскими объектами, сделали RPC вызовы и прочее, позвольте мне рассказать о менее увлекательной теме: обработке ошибок. При любой сетевой транзакции может произойти множество сбоев. Не запускается сервис, ошибка в работе сервера, не удается найти объект, отсутствуют классы и множество прочих проблем. До сих пор я просто использовал метод fault.getString() для генерации сообщений об ошибках. Но этот метод не всегда может оказаться полезным. Чтобы увидеть его в действии, снимите комментарий в конструкторе CDCatalog :

public CDCatalog() { //catalog = new Hashtable(); // Создание каталога addCD(new CD("Nickel Creek", "Nickel Creek", "Sugar Hill")); addCD(new CD("Let it Fall", "Sean Watkins", "Sugar Hill")); addCD(new CD("Aerial Boundaries", "Michael Hedges", "Windham Hill")); addCD(new CD("Taproot", "Michael Hedges", "Windham Hill")); }

Перекомпилируйте его, перезапустите движок сервлета и переразместите его. В результате этого возникнет исключительная ситуация NullPointerException при попытке конструктора класса добавить CD в неинициализированный Hashtable . При запуске клиента появится сообщение об ошибке, но оно будет не очень информативным:

(gandalf)/javaxml2/build$ java javaxml2.CDLister http://localhost:8080/soap/servlet/rpcrouter Просмотр текущего каталога CD. Ошибка: Не удается разрешить целевой объект(Unable to resolve target object): null

Это совсем не та информация, которая может помочь при обнаружении и исправлении ошибки. Тем не менее фреймуорк исправно справляется с обработкой ошибок. Вы помните DOMFaultListener , который вы задавали как значение элемента faultListener ? Настал его час вступить в игру. Возвращаемый в случае ошибки объект Fault содержит DOM (объектную модель документа) org.w3c.dom.Element с детальной информацией об ошибке. Сначала добавьте в исходный код выражение для импортирования java.util.Iterator :

import java.net.URL; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.Vector; import org.apache.soap.Constants; import org.apache.soap.Fault; import org.apache.soap.SOAPException; import org.apache.soap.encoding.SOAPMappingRegistry; import org.apache.soap.encoding.soapenc.BeanSerializer; import org.apache.soap.rpc.Call; import org.apache.soap.rpc.Parameter; import org.apache.soap.rpc.Response; import org.apache.soap.util.xml.QName;

Теперь внесем изменения для обработки ошибок в методе list():

if (!response.generatedFault()) { Parameter returnValue = response.getReturnValue(); Hashtable catalog = (Hashtable)returnValue.getValue(); Enumeration e = catalog.keys(); while (e.hasMoreElements()) { String title = (String)e.nextElement(); CD cd = (CD)catalog.get(title); System.out.println(" "" + cd.getTitle() + "" исполнителя " + cd.getArtist() + " студии " + cd.getLabel()); } } else { Fault fault = response.getFault(); System.out.println("Ошибка: " + fault.getFaultString()); Vector entries = fault.getDetailEntries(); for (Iterator i = entries.iterator(); i.hasNext();) { org.w3c.dom.Element entry = (org.w3c.dom.Element)i.next(); System.out.println(entry.getFirstChild().getNodeValue()); } }

Используя метод getDetailEntries() вы получаете доступ к поддерживаемым сервисом SOAP и сервером с необработанным данным, с информацией о проблеме. Код повторно обрабатывает их (как правило существует только один элемент, но он требует пристального внимания) и перехватывает DOM Element , содержащийся в каждой записи. По сути дела, вот XML с которым вы работаете:

SOAP-ENV:Server.BadTargetObjectURI Не удается разрешить целевой объект: null Вот то, чего мы хотим!

Иными словами, объект Fault предоставляет вам доступ к части конверта SOAP, которая содержит ошибки. Кроме того, Apache SOAP обеспечивает трассировку стека Java в случае возникновения ошибок, предоставляющую детализованную информацию, необходимую для их исправления. Перехватив элемент stackTrace и распечатав значение узла Text из этого элемента ваш клиент может распечатать трассировку стека сервера. Скомпилировав эти изменения и перезапустив клиент вы получите следующий результат:

C:\javaxml2\build>java javaxml2.CDLister http://localhost:8080/soap/servlet/rpcr outer Просмотр текущего каталога CD. Ошибка: Не удается разрешить целевой объект: null java.lang.NullPointerException в javaxml2.CDCatalog.addCD(CDCatalog.java:24) в javaxml2.CDCatalog.(CDCatalog.java:14) в java.lang.Class.newInstance0(Native Method) в java.lang.Class.newInstance(Class.java:237)

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

  1. Ведется множество разговоров относительно запуска SOAP через другие протоколы, например SMTP (или даже Jabber). Пока стандарт SOAP не предусматривает этого, но подобные возможности могут быть добавлены в будущем. Поэтому не удивляйтесь если встретитесь с активными обсуждениями этой темы.
  • 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-схемой (возможно, и другие сервера этого не делают).

Не буду останавливаться на вопросе, что такое веб-сервисы и зачем они нужны. В сети очень много статей на эту тему. Просто постараюсь вкратце показать, каким простым способом возможно создание клиента к любому веб-сервису на php.

Настройка

Для использования SOAP в php необходимо подключить модуль SOAP (входит в дистрибутив php5). Под windows это делается просто – необходимо дописать (именно дописать, так как эта строка там не просто закомментирована, она отсутствует вообще) в php.ini :
extension=php_soap.dll

Не забудьте перезапустить сервер, если php у вас установлен как модуль.


Создание SOAP-клиента по WSDL-документу

Создание SOAP-клиента обычно происходит по WSDL-документу , который представляет собой XML-документ в определенном формате, полностью описывающий тот или иной веб-сервис. За подробностями по поводу WSDL – отправляю Вас на сайт консорциума W3C - http://www.w3.org/TR/2005/WD-wsdl20-soap11-binding-20050510/ .

Главное же, что необходимо знать для того, чтобы построить клиента к веб-сервису – это знать URL его WSDL-документа.
Для примера возьмем веб-сервис "Currency Exchange Rate" от xmethods.com. Адрес этого веб-сервиса, который позволяет получать курсы валют в режиме онлайн - http://www.xmethods.net/sd/2001/CurrencyExchangeService.wsdl .

Второй важный момент – из описания веб-сервиса необходимо получить информацию о том, какие методы этот сервис предоставляет, и какие параметры мы должны передавать ему в качестве входных значений (очень похоже на вызов обычной функции php или метода класса). Обычно эта информация содержится в описании сервиса на его сайте. Наш веб-сервис для получения курса валют предоставляет метод getRate(), которому в качестве аргументов передаются коды валют.

И последнее – важно знать, что ожидать в качестве ответа: сколько значений, какого типа и т.п. Это также можно получить из описания.
А в результате код получается очень простым и компактным, почти элементарным:

// Использование Web-сервиса
// "Currency Exchange Rate" от xmethods.com

// Создание SOAP-клиента по WSDL-документу
$client = new SoapClient("http://www.xmethods.net/sd/2001/CurrencyExchangeService.wsdl");

// Поcылка SOAP-запроса и получение результата
$result = $client->getRate("us", "russia");

Echo ‘Текущий курс доллара: ’, $result, ‘ рублей’;
?>

Как видно из кода в конструктор класса SoapClient необходимо передать URL WSDL-документа и получить объект для работы с нужным веб-сервисом. Затем вызывается метод этого объекта, имя которого совпадает с именем самого метода веб-сервиса. Возвращает же этот метод желаемый нами результат.

Итак, этот простой пример иллюстрирует нам принцип построения SOAP-клиента для веб-сервисов на php. Однако в реальном приложении еще о многом придется позаботиться, в частности о том, что в момент обращения к веб-сервису он может быть временно недоступен или возвращать ошибку. Явно напрашивается использование блока try/catch/throw :)

Here at LeaseWeb, we work a lot with SOAP web-services to integrate our internal applications with each other. Especially during development and testing of our applications as we need the ability to practice with SOAP API’s.

$ curl -sS http://leaseweb.github.io/php-soap-client/installer | php

This will download the phar file to the current working directory and make it executable so you can use start using it right away by invoking:

$ ./soap_client

To install the latest master version you can get the source code directly from GitHub , package your own .phar file and install it — using GNU Make .
In order to be able to create the .phar file you need to have composer installed. To read more about composer refer to their excellent documentation .

# Install php soap client $ git clone https://github.com/LeaseWeb/php-soap-client.git $ cd php-soap-client $ composer.phar install $ make $ sudo make install

If you are getting a Failed to compile phar exception while running make you need to set phar.readonly = Off in your php.ini . On a development machine this is fine to do but please be ware of the security risks when setting phar.readonly to Off .

The above make install command will install the soap_client application to /usr/local/bin and make it executable so you can easily call it like this:

$ soap_client php-soap-client version 2.1.3 Usage: command Options: ... Available commands: call Call the remote service with the `method` specified and output the reponse to stdout. help Displays help for a command list Lists commands list-methods Get a list of available methods to call on the remote. request Generate an xml formatted SOAP request for the given method and output to stdout. wsdl Get the WSDL of a soap service.

From this point onwards we assume you have installed the soap_client.phar on your system in /usr/local/bin/soap_client and that the directory /urs/local/bin is in your $PATH .

Lets say we would like to see what methods are available on the remote service http://www.webservicex.net/ConvertTemperature.asmx . We could issue the following command:

$ soap_client --endpoint="http://www.webservicex.net/ConvertTemperature.asmx?WSDL" list-methods

Which will output the following:

ConvertTemp

If you run the above command with the -vvv option you will get more verbose output.
In this case the only available method is ConvertTemp . Let’s see how a SOAP XML request looks like for this method:

$ soap_client --endpoint="http://www.webservicex.net/ConvertTemperature.asmx?WSDL" request ConvertTemp 0

If you want to make a SOAP request to the ConvertTemp method on the remote service use the call sub command:

$ soap_client --endpoint="http://www.webservicex.net/ConvertTemperature.asmx?WSDL" call --editor ConvertTemp

Notice the --editor option after the call sub command. If you use the --editor flag soap_client will open up the editor specified in your environment variable $EDITOR so you are able to modify the request XML before sending it.

If you issue the same request multiple times, you could save a soap request as a local XML file and pass it to /dev/stdin of the soap_client call command:

# Get the request xml and store it locally $ soap_client --endpoint="http://www.webservicex.net/ConvertTemperature.asmx?WSDL" request ConvertTemp > my_sample_request.xml # Now edit my_sample_request.xml # Now you can call the ConvertTemp method with this pre-prepared request $ soap_client --endpoint="http://www.webservicex.net/ConvertTemperature.asmx?WSDL" call ConvertTemp < my_sample_request.xml

Since you will be repeating soap_client commands frequently in a short time while exploring a remote web service you can save yourself some time by setting an environment variable SOAPCLIENT_ENDPOINT that contains the URL to the WSDL. When this environment variable is set you can omit the --endpoint command line option. Let’s do this now and call the ConvertTemp method:

$ export SOAPCLIENT_ENDPOINT="http://www.webservicex.net/ConvertTemperature.asmx?WSDL" $ soap_client call ConvertTemp < my_sample_request.xml

I wanted to know how much 107.6 degrees Fahrenheit is in Celsius, so my my_sample_request.xml contains:

$ cat my_sample_request.xml 107.6 degreeFahrenheit degreeCelsius

$ soap_client call ConvertTemp < my_sample_request.xml stdClass Object ( => 42)

The answer is 42.

If you rather see the responses in XML format you can use the --xml command line option:

$ soap_client call --xml ConvertTemp < my_sample_request.xml 42

This tutorial should give you enough information to get started with exploring, testing and/or developing SOAP API’s.
In a future blog post, I will continue the topic of the php soap client . We are currently working on packing the .phar archive for the web.

Лирическая часть.

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

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

Другие системы, обращаясь к этому серверу уже могут распоряжаться полученными от этого сервера данными по своему усмотрению - обрабатывать, накапливать, выдавать своим клиентам и т.д.

Ну вот, один из вариантов общения с такими серверами - это SOAP. SOAP протокол обмена xml-сообщениями.

Практическая часть.

Веб-сервис (так называется то, что предоставляет сервер и то, что используют клиенты) дает возможность общения с сервером четко структурированными сообщениями. Дело в том, что веб-сервис не принимает абы какие данные. На любое сообщение, которое не соответствует правилам, веб-сервис ответит ошибкой. Ошибка будет, кстати, тоже в виде xml с четкой структурой (чего нельзя сказать правда о тексте сообщения).

WSDL (Web Services Description Language). Правила, по которым составляются сообщения для веб-сервиса описываются так же с помощью xml и также имеют четкую структуру. Т.е. если веб-сервис предоставляет возможность вызова какого-то метода, он должен дать возможность клиентам узнать какие параметры для данного метода используются. Если веб-сервис ждет строку для метода Method1 в качестве параметра и строка должна иметь имя Param1, то в описании веб-сервиса эти правила будут указаны.

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

Для клиентов достаточно знать url веб-сервиса, wsdl всегда будет рядом, по которому можно получить представление о методах и их параметрах, которые предоставляет этот веб-сервис.

Какие плюсы у всех этих наворотов:

  • В большинстве систем описание методов и типов происходит в автоматическом режиме. Т.е. программисту на сервере достаточно сказать, что данный метод можно вызывать через веб-сервис, и wsdl-описание будет сгенерировано автоматом.
  • Описание, имеющее четкую структуру, читается любым soap-клиентом. Т.е. какой бы ни был веб-сервис, клиент поймет какие данные веб-сервис принимает. По этому описанию клиент может построить свою внутреннюю структуру классов объектов, т.н. binding"и. В итоге программисту, использующему веб-сервис, остается написать что-то типа (псевдокод):

    NewUser:=TSoapUser.Create("Вася","Пупкин","odmin"); soap.AddUser(NewUser);

  • Автоматическая валидация.

    • xml-валидация. xml должен быть well-formed. невалидный xml - сразу ошибка клиенту, пусть разбирается.
    • schema-валидация. xml должен иметь определенную структуру. xml не соответствует схеме - сразу ошибка клиенту, пусть разбирается.
    • проверка данных осуществляется soap-сервером, чтобы типы данных, ограничения соответствовали описанию.
  • Авторизация и аутентификация может быть реализована отдельным методом. нативно. либо, используя http-авторизацию.
  • Веб-сервисы могут работать как по soap-протоколу, так и по http, то есть через get-запросы. То есть, если в качестве параметров идут простые данные (без структуры), то можно вызвать просто обычный get www.site.com/users.asmx/GetUser?Name=Vasia или post. Впрочем это не везде и не всегда.
  • ... см. в википедии

Минусов тоже полно:

  • Неоправданно большой размер сообщений. Ну тут сама природа xml такова, что формат избыточный, чем больше тэгов, тем больше неполезной информации. Плюс soap добавляет своей избыточности. Для intranet-систем вопрос трафика стоит менее остро, чем для internet, поэтому soap для локальных сетей более востребован, в частности у Sharepoint есть soap веб-сервис, с которым с успехом (и некоторыми ограничениями) можно общаться.
  • Автоматическая смена описания веб-сервиса может сломать все клиенты. Ну это как бы для любой системы так, если не поддерживается обратная совместимость со старыми методами, все отвалится...
  • Не минус, но недостаток. Все действия по вызову методов должны быть атомарными. Например, работая с субд мы можем начать транзакцию, выполнить несколько запросов, потом откатиться или закоммитить. В soap транзакций нет. Один запрос-один ответ, разговор закончен.
  • Разбираться с описанием, что на стороне сервера (все ли правильно описано у меня?), что на клиенте (что мне тут наописывали?) бывает довольно сложно. Было несколько раз, когда мне приходилось разбираться с клиентской стороны, и убеждать серверного программера, что у него неверно описаны данные, а он в них вообще ничего понять не мог, ибо автоматическая генерация и он как бы и не должен, это дело софта. А ошибка естественно была в коде метода, программер ее не видел просто.
  • Практика показывает, что разработчики веб-сервисов страшно далеки от народа, использующего эти веб-сервисы. В ответ на какой-либо запрос (валидный со стороны) может прийти невразумительная ошибка "Ошибка 5. Все плохо". Все зависит от совести разработчиков:)
  • еще наверняка что-то не вспомнил...

В качестве примера есть открытый веб-сервис belavia:

  • http://86.57.245.235/TimeTable/Service.asmx - точка входа, там же текстовое описание методов для сторонних разработчиков.
  • http://86.57.245.235/TimeTable/Service.asmx?WSDL - wsdl описание методов и типов принимаемых и возращаемых данных.
  • http://86.57.245.235/TimeTable/Service.asmx?op=GetAirportsList - описание конкретного метода с примером вида xml-запроса и xml-ответа.

Можете вручную создать и послать запрос типа:

POST /TimeTable/Service.asmx HTTP/1.1 Host: 86.57.245.235 Content-Type: text/xml; charset=utf-8 Content-Length: length SOAPAction: "http://webservices.belavia.by/GetAirportsList" ru

в ответ придет:

HTTP/1.1 200 OK Date: Mon, 30 Sep 2013 00:06:44 GMT Server: Microsoft-IIS/6.0 X-Powered-By: ASP.NET X-AspNet-Version: 4.0.30319 Cache-Control: private, max-age=0 Content-Type: text/xml; charset=utf-8 Content-Length: 2940

ЗЫ Раньше был открыт веб-сервис аэрофлота, но после того как 1C добавили поддержку soap в 8ку, куча 1с-бета-тестеров с успехом положили его. Сейчас что-то там поменяли (адреса не знаю, можно поискать, если интересно).
ЗЗЫ Дисклеймер. Рассказал на бытовом уровне. Пинать можно.

просмотров