Давненько я собирался разобраться с протоколом SOAP, посмотреть как с ним работать, как использовать и т.д., но то за неимением большого количества свободного времени, то из-за обычного нежелания заморачиваться над чем-то новым всё никак не мог добраться до написания какого-нибудь приложения с использованием SOAP. Обычно, когда я разбирался с каким-либо API Web-сервиса у которого на выбор было два протокола – чистый HTTP или с SOAP я выбирался первый и, собственно сегодня понял, что зря :).
Оказалось, что разработать небольшой клиент с SOAP в Delphi элементарно и на порядок проще, чем использовать отдельно библиотеку для работы с HTTP (Synapse, WinInet, Indy и т.д.) в связке с библиотеками типа MSXML, NativeXML (для работы с XML) или SuperObject (для JSON).
В качестве примера я решил написать небольшой клиент для работы с Bing Translator API (переводчик Google уже был – теперь решил посмотреть, что там насочиняли в Microsoft).
Для начала несколько вводных слов по тому, что из себя представляет протокол SOAP.
Выписка из Wiki:
SOAP (от англ. Simple Object Access Protocol — простой протокол доступа к объектам) — протокол обмена структурированными сообщениями в распределённой вычислительной среде. Первоначально SOAP предназначался в основном для реализации удалённого вызова процедур (RPC). Сейчас протокол используется для обмена произвольными сообщениями в формате XML, а не только для вызова процедур.
На той же странице Wiki можно узнать и про недостаток SOAP – увеличение объема пересылаемых данных. Но нам сегодня это не так важно, нам бы просто разобраться с работой.
Примечание: Если же для вас очень важен объем пересылаемых данных, то можно привести следующую классификацию обмена данными в различных форматах (за исключением Plain Text) и с использованием различных протоколов (из тех, что мне сейчас более менее известны):
- HTTP + SOAP – наибольший объем;
- HTTP + XML – средний объем;
- HTTP + JSON – наименьший объем;
Все типы данных, операции, способы доставки сообщений от сервера к клиенту и обратно расположены на стороне сервера в виде WSDL-документа. Все, что от нас необходимо – это импортировать каким-либо образом этот документ и представить его в виде уже известных нам типов, классов и объектов Delphi.
Так как WSDL – это стандартный язык описания web-сервисов, то, соответственно, в Delphi предусмотрен удобный импорт таких документов и представление их в необходимой нам форме, т.е. в виде отдельного юнита Delphi.
Теперь перейдем непосредственно к рассмотрению примера работы с Bing API через SOAP.
Прежде, чем мы запустим Delphi и начнем разрабатывать наш мини-переводчик нам необходимо зарегистрироваться в Bing и на странице для разработчиков получить ключ доступа (AppID) к API. Регистрируемся, получаем и запоминаем наш AppID.
Теперь запускаем Delphi (у меня стоит Delphi XE) и выбираем в меню File –> New –> Other. В открывшемся окне выбираем WebServices –> WSDLImporter.
WSDLImporter представляет собой мастера, который максимально удобно и аккуратно импоритирует нам все методы, типы и классы сервиса нам в Delphi. Для начала мастер попросит нас ввести путь к WSDL-документу и, если это необходимо – данные для авторизации:
Путь к WSDL-документу Bing Translator API указан на первой странице документации и выглядит следующим образом:
http://api.microsofttranslator.com/V2/Soap.svc
“Скармливаем” его мастеру и жмем “Next”:
Мастер просит нас выбрать версию SOAP, которую мы будем использовать. Оставляем значение по умолчанию и жмем “Next”:
Здесь мастер нас спросит какие опции WSDL нам необходимо импортировать. В принципе того, что выбрано по умолчанию нам вполне достаточно, поэтому со спокойной душой жмем “Finish” и мастер создает нам вполне понятный юнит с названием soap.pas. На этом работа с WSDLImporter заканчивается и приступаем к реализации функций переводчика.
Создаем обычное приложение (VCL Forms Application) и подключаем в uses только что созданный модуль soap.pas.
Бросаем также на форму компоненты TButton, TListBox и компонент THTTPRIO со страницы (WebServices).
Теперь открываем документацию по SOAP Bing API и попробуем реализовать несколько различных функций. Начнем с функций, возвращающих строки, например, воспользуемся методом Microsoft.Translator.Detect. Смотрим его описание в модуле SOAP.pas:
LanguageService = interface(IInvokable) ['{605B2735-9E5A-5D06-DFDA-8FBEA3B18556}'] ... function Detect(const appId: string; const text: string): string; stdcall; ... end;
Вызвать такой метод для нас намного проще и легче, чем скажем, использовать связку HTTP+XML — сначала сформировать URL, потом выполнить запрос и получить ответ, потом парсить XML и выводить результат. Но остается один вопрос: как собственно отправить этот самый запрос? Именно для этого мы и уложили на форму компонент THTTPRIO. Остается только его настроить следующим образом:
В поле WSDLLocation записываем тот же URL, что и при импорте WSDL-документа (см. выше) и в полях Port и Service выбираем единственные доступные значения. Должно получиться как показано на рисунке:
И теперь самое интересное – если вы посмотрите список методов компонента HTTPRIO, то не обнаружите в нем таких привычных при работе с HTTP методов как POST, GET и др. Они нам собственно и не нужны – у нас есть интерфейс LanguageService с помощью которого мы и получим необходимые нам данные.
Создаем обработчик OnClick кнопки и пишем:
ShowMessage((HTTPRIO1 as LanguageService).Detect('YouAppID','some text'))
Запускаем приложение, жмем кнопку и видим сообщение:
Запрос отправлен, ответ получен. Без парсинга XML и JSON и прочих обычных операций при работе с HTTP. Неправда ли достаточно просто и удобно? :). Со сложными типами данных так же просто работать, т.к. их описание содержится в модуле. Например, можно воспользоваться методом Microsoft.Translator.GetLanguageNames, который возвращает нам массив строк — названий языков на языке пользователя. Смотрим описание метода в soap.pas:
function GetLanguageNames(const appId: string; const locale: string; const languageCodes: ArrayOfstring): ArrayOfstring; stdcall;
Единственный тип данных, который может быть нам непонятен — это ArrayOfString. Смотрим его описание в модуле:
ArrayOfstring = array of string;
Пишем обработчик кнопки. Например, так:
procedure TForm1.Button1Click(Sender: TObject); var LangNames: ArrayOfstring; Names: ArrayOfstring; i:integer; begin SetLength(LangNames,5); LangNames[0]:='fr'; LangNames[1]:='en'; LangNames[2]:='ru'; LangNames[3]:='de'; LangNames[4]:='ko'; Names:=(HTTPRIO1 as LanguageService).GetLanguageNames('YouAppID','ru',LangNames); ListBox1.Items.Clear; for i:=0 to Length(Names)-1 do ListBox1.Items.Add(Names[i]); end;
Запускаем программу, жмем кнопку и видим результат в ListBox:
Если в массиве LangNames будет содержаться “непонятная” серверу строка, то ошибки не возникнет, а результат работы метода будет содержать на 1 элемент меньше.
И, в заключение этой небольшой статьи, попробуем перевести какой-нибудь текст. Для этого воспользуемся методом Microsoft.Translator.GetTranslations, который возвращает нам все возможные переводы текста. Напишем такой обработчик у кнопки:
procedure TForm1.Button1Click(Sender: TObject); var i:integer; Opt: TranslateOptions; Result : GetTranslationsResponse; begin Opt:=TranslateOptions.Create; Result:=(HTTPRIO1 as LanguageService).GetTranslations('YouAppID','Memory','en','ru',10,Opt); for i:=0 to Length(Result.Translations)-1 do ListBox1.Items.Add(Result.Translations[i].TranslatedText) end;
Результат выполнения программы будет следующим:
Вот, пожалуй и все. Думаю, что в качестве небольшой шпаргалки по работе с Bing API через SOAP в Delphi этот пост сгодится. Можете попрбовать поработать с API переводчика от Microsoft.
Книжная полка
Описание: Рассмотрены практические вопросы по разработке клиент-серверных приложений в среде Delphi 7 и Delphi 2005 с использованием СУБД MS SQL Server 2000, InterBase и Firebird. Приведена информация о теории построения реляционных баз данных и языке SQL. Освещены вопросы эксплуатации и администрирования СУБД.
|
||
Название: О чем не пишут в книгах по Delphi
Описание: Рассмотрены малоосвещенные вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные режимы их работы, особенности для протоколов TCP и UDP и др.
|
Здравствуйте ув. Автор!
Очень нужна Ваша помощь.
Мне нужно организовать общение с интернет магазинами на движке PrestaShop по REST протоколу. В частности отправлять данные в магазин. Здесь есть описание API: http://www.sendspace.com/file/6tqj4h
Пытался сделать WSDLImporter. В пути к WSDL-документу указал путь к api с учётом ключа, как описано в документации: http://TNP0ETIYA2KY6RU387GKKWJNSUX8OT94@prestashop2/api/
но в ответ получаю ошибку 401.
Помогите разобраться в моей проблеме. Я ещё пока не силён в REST.
1. REST — не протокол, а подход к архитектуре сетевых протоколов, обеспечивающих доступ к информационным ресурсам (см. вики).
2. REST SOAP.
3. Ссылки в сообщении какие-то «левые» — одна на файлообменник, вторая вообще не работает.
В общем, если API не работает с SOAP, то берете простой Indy или Synapse, вооружаетесь кучей терпения, читаете мануалы по api и составляете согласно мануалу правильные GET и POST-запросы к API и анализируете результаты. Вот вам и весь секрет работы с REST
Добрый день.
procedure TForm1.Button1Click(Sender: TObject);
var LangNames: ArrayOfstring;
Names: ArrayOfstring;
i:integer;
begin
SetLength(LangNames,5);
LangNames[0]:=’fr’;
LangNames[1]:=’en’;
LangNames[2]:=’ru’;
LangNames[3]:=’de’;
LangNames[4]:=’ko’;
Names:=(HTTPRIO1 as LanguageService).GetLanguageNames(‘YouAppID’,’ru’,LangNames);
ListBox1.Items.Clear;
for i:=0 to Length(Names)-1 do
ListBox1.Items.Add(Names[i]);
end;
Возникает runtime ошибка
«Value cannot be null.Parameter name: languageCodes»
Здравствуйте!
Выложите пожалуйста soap.pas, а то у меня WSDL Importer не пашет а Гугл молчит по всем (что только смог придумать) вариантам решения данного затруднения.
Уже не надо, импортнул, но не проверял работу.
Там где показан импортер у вас на сайте у меня тоже есть такая иконка, только она не активная… и естественно он не запускается.
По чистой случайности нашел данный импортер по другому адресу: Component -> Import WSDL
Поддерживаю Taron:
Тоже, возникает runtime ошибка«Value cannot be null.Parameter name: languageCodes»
Притом LangNames может быть действительно пустым, или получен через GetLanguagesForTranslate, или как в примере — результат один — ошибка. Остальные методы, куда НЕ передаётся тип ArrayOfString работают корректно. Такое ощущение, что динамический массив всегда определяется как пустой и на сервис передаётся null вместо нормального массива. Интересно, что при этом GetLanguagesForTranslate заполняет динамический массив корректно, но это не вход, а выход )
Ошибка решилась очень «экстравагантным методом» ))
Под Delphi 2010 — ошибка есть, под Delphi XE2U3 — ошибки нет ))
Embarcadero, лучше поздно, чем никогда ))
Почему этот пример не работает на Delphi2009 а на Delphi XE2 работает? Не только этот пример даже если сам написал SOAP сервер клиент написанный на Delphi2009 не работает!
oshibka
Result:=(HTTPRIO1 as LanguageService).GetTranslations(‘YouAppID’,’Memory’,’en’,’ru’,10,Opt)
Dcc error: incompatible types ‘getTranslationsResponse’ and ‘getTranslationsResponse2’
Спасибо за статью, реально просто выручила, столько мучился, а тут прям как раз нашел разъяснение всех интересующих нюансов. Еще раз спасибо!
А как при реализации веб-сервиса в функции определить обязательный/необязательный параметр?
Ограничение: 2 млн. символов в месяц.
Пруфф
Спустя два года появилось такое ограничение, да. И OAuth-авторизация тоже появилась