Т.к. предоставить wsdl-файлы, с которыми велась работа, я не могу (NDA и все такое), то я задался поиском сервисов, пригодных для тестирования. Интересными мне показались два:
http://www.webservicex.net/ValidateEmail.asmx?WSDL
http://www.webservicex.net/country.asmx?WSDL
Я так и не нашел, где скачать wsdl-файлы, поэтому скопировал их содержимое и сохранил под именами ValidateEmail.wsdl и country.wsdl
Скачать gSOAP можно тут — http://www.cs.fsu.edu/~engelen/soap.html. По этому же адресу можно и почитать о gSOAP.
Последняя версия на момент написания статьи — 2.8.14
Приступим к работе. В папке gsoap\bin\win32\ живут две очень важные утилиты. Сначала нас интересует wsdl2h.exe. Узнать о ней побольше можно с помощью справки:
>wsdlh2.exe -h
Запустим ее со следующими параметрами:
wsdl2h.exe -o emailAndCountry.h ValidateEmail.wsdl country.wsdl
Все довольно просто, мы указали лишь имя выходного файла и список wsdl-файлов, с которыми хотим работать.
После этого генерируем непосредственно код классов C++:
soapcpp2.exe -C -dgSoap -j -L -x -I«ADDRESS_TO_GSOAP\gsoap-2.8\gsoap\import» emailAndCountry.h
Ключ -C говорит, что нужно генерировать только клиентский код.
-dgSoap просит сложить все файла в папку gSoap (ее нужно предварительно создать). Мы не генерируем lib-файлы и не наследуемся от soap-структуры; ключ -x просит не генерировать XML-файлы с примерами сообщений. Указываем адрес до папки с gSOAP и файл, который будем парсить и на основе которого генерируем код.
gSOAP разных версий может генерировать разный код (что логично, в общем-то), причем даже состав файлов будет различаться. Это важно помнить, если вы вдруг захотите генерировать gSOAP-файлs на билд-сервере, а не хранить их в системе контроля версий.
После всех манипуляций в папке gSoap видим много новых файлов. Это плюсовые -h и -cpp файла, а так же countrySoap.nsmap и ValidateEmailSoap.nsmap. Они совпадают, можно сохранить их содержимое в одном (например, namespaces.nsmap), а их удалить. namespaces.nsmap нужно заинклудить в проект. Как правило, это делается в каком-нибудь вспомогательном классе, который будет рабоать с gSOAP. Да, такой класс наверняка будет существовать.
После этого добавляем в папку gSoap stdsoap2.h и stdsoap2.cpp — они инклудятся в soapStub.h
Добавляем всю папку в проект и начинаем работать:)
Смотрим классы soapcountrySoapProxy.cpp и soapValidateEmailSoapProxy.cpp; в методах *_init(soap_mode imode, soap_mode omode) — удаляем namespases (мы же не зря инклудили наш namespaces.nsmap).
Начнем с возможности валидации е-мейла — gSoap/soapValidateEmailSoapProxy.h
Нас интересует метод
virtual int IsValidEmail(_ns1__IsValidEmail *ns1__IsValidEmail, _ns1__IsValidEmailResponse *ns1__IsValidEmailResponse)
Смотрим описание аргументов этой функции:
class SOAP_CMAC _ns1__IsValidEmail { public: std::string *Email; /* optional element of type xsd:string */ struct soap *soap; /* transient * …./
Поле *Email нам наверняка пригодится.
Создаем объект этого класса и указываем объект, который будем проверять
_ns1__IsValidEmail isValidEmailRequest; std::string CHECKED_E_MAIL("pisem@sovsem.net"); isValidEmailRequest.Email = &CHECKED_E_MAIL;
Сразу создадим объект, в который будет приходить результат:
_ns1__IsValidEmailResponse isValidEmailResponse;
Мы видим, что результат будет в поле bool IsValidEmailResult.
Отправляем запрос на сервер:
const int gSoapResult = validateEmailProxy.IsValidEmail(&isValidEmailRequest, &isValidEmailResponse);
Сниффером смотрим, что именно мы отправили:
<?xml version="1.0" encoding="UTF-8" ?> - <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns1="http://www.webservicex.net" xmlns:ns2="http://www.webserviceX.NET"> - <SOAP-ENV:Body> - <ns1:IsValidEmail> <ns1:Email>pisem@sovsem.net</ns1:Email> </ns1:IsValidEmail> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
Смотрим ответ — видим, что в поле result находится false. Сниффер это подтверждает.
Пробуем другой адрес — press@fsb.ru
Сразу замечу, что сервер думает довольно-таки долго, но с проверяемым адресом это вряд ли связано.
Но ответ все же приходит, и этот адрес у нас считается невалидным.
Что ж, пробуем заведомо “плохой” адрес — “1354@”
Время выполнения результата — вечность. Работает действительно очень долго.
Примерно через минуту мне ждать надоело, и я решил снова попробовать проверить валидный адрес — adv@thematicmedia.ru
Ок, результат получен — false.
adv@thematicmedia — false
adv@l — false
Попробовал один из своих древних почтовых ящиков — сервер опять задумался. Надолго. Но все-таки ответил — false.
Ну что ж, получается, что проблема лишь в сервере, наш код ответы сервера передает верно. Код работает, но абсолютно бесполезен.
Пробуем второй класс — countrySoapProxy
Количество его методов значительно больше, их можно посмотреть в хидере.
Будем тестировать их по порядку, начиная с GetCountryByCountryCode:
_ns2__GetCountryByCountryCode getCountryByCountryCodeRequest; std::string COUNTRY_CODE("GB"); getCountryByCountryCodeRequest.CountryCode = &COUNTRY_CODE; _ns2__GetCountryByCountryCodeResponse getCountryByCountryCodeResponse; countrySoapProxy countryProxy; const int gSoapResult = countryProxy.GetCountryByCountryCode(&getCountryByCountryCodeRequest, &getCountryByCountryCodeResponse); if (gSoapResult != SOAP_OK) { std::cout << "FAIL" << std::endl; return 0; }
Ответ получен:
<NewDataSet> <Table> <countrycode>gb</countrycode> <name>Great Britain</name> </Table> <Table> <countrycode>gb</countrycode> <name>Great Britain</name> </Table> </NewDataSet>
Я, признаться, ожидал, в ответе будет лишь страна, а не здоровый DataSet, ну да это придирки. Страну с кодом RU этот сервис тоже знает, ура!
Переходим к другому методу:
_ns2__GetISD getISDRequest; std::string COUNTRY_NAME("Russian Federation"); getISDRequest.CountryName = &COUNTRY_NAME; _ns2__GetISDResponse getISDResponse; countrySoapProxy countryProxy; const int gSoapResult = countryProxy.GetISD(&getISDRequest, &getISDResponse);
И этот метод работает как надо:
<NewDataSet> <Table> <code>7</code> <name>Russian Federation</name> </Table> <Table> <code>7</code> <name>Russian Federation</name> </Table> </NewDataSet>
Что ж, для этого сервиса все работает, как ожидалось, для учебных целей он подходит чуть больше, чем первый. На этом эксперименты можно пока и прекратить.
Такой подход замечательно работает, когда мы собираемся работать только с одним сервисом, который расположен по заданному адресу и не требует авторизации.
Но мы же можем развернуть веб-сервис где угодно и как угодно!
На самом деле, gSoap умеет многое. Так, например, задать логин-пароль можно через soap-структуру. Пример простейшей basic-авторизации:
soap.userid = login; soap.passwd =password;
Описание soap-структуры находится в stdsoap2.h:
const char *userid; /* HTTP Basic authorization userid */ const char *passwd; /* HTTP Basic authorization passwd */
Изменить адрес, на который будут отправляться запросы, можно двумя способами: задавать его при самом запросе либо задавать при создании SOAP-прокси.
Все это довольно очевидно из самих хидеров:
virtual int GetISD(_ns2__GetISD *ns2__GetISD, _ns2__GetISDResponse *ns2__GetISDResponse) { return this->GetISD(NULL, NULL, ns2__GetISD, ns2__GetISDResponse); } virtual int GetISD(const char *endpoint, const char *soap_action, _ns2__GetISD *ns2__GetISD, _ns2__GetISDResponse *ns2__GetISDResponse);
В случае второго способа нам надо создавать объект примерно так:
countrySoapProxy countryProxy("http://www.webservicex.net/country.asmx");
Тоже ничего сложного, не так ли? Здесь можно задавать и нормальный IP-адрес с портом.
Еще я не учел, как самому задавать нэймспейсы. Такой подход позволит получать не ns1__IsValidEmail в качестве имен классов, а что-то вроде email__IsValidEmail. Когда классов много, то можно запутаться. Наверное. Это делается тоже без проблем. Создаем файл typeMap.dat c содержимым следующего формата:
myCustomNamespace = «www.webservicex.net»
Т.е. все просто: указываем имя нэймспейса и его адрес.
Вообще, работа с gSoap не составляет особой трудности. Но есть несколько моментов, которые всплывают при работе с ним. Так, например, при задании логина и пароля они могут меняться после выполнения запроса. Т.е. код может выглядеть примерно так:
countrySoapProxy countryProxy("http://www.webservicex.net/country.asmx"); countryProxy.soap->userid = "login"; countryProxy.soap->passwd = "password"; countryProxy.GetISD(&getISDRequest, &getISDResponse); countryProxy.soap->userid = "login"; countryProxy.soap->passwd = "password"; countryProxy.GetISD(&anotherGetISDRequest, &anotherGetISDResponse);
Выглядит это почему-то сомнительно.
Возможно, я чего-то не учел, но краткий опыт работы с gSOAP оставляет смешанное впечатление: высокая скорость разработки, относительно небольшое количество кода — все это в плюс. Но вот мешанина кода, смесь С и С++ — это минус. Но я допускаю, что это минус не для всех, а плюсов значительно больше. В любом случае, достойных альтернатив gSOAP мне найти не удалось — хотя я искал не очень тщательно, доверившись проверенному в нашей компании решению.
Эта статья — скорее вводная, которая кратко рассказывает о начале работы с gSOAP. Я не привел здесь разных способов авторизации, не изучил вопросы наследования от SOAP-структур (это же для чего-то нужно?). Нераскрытых вопросов много, но краткий экскурс в gSOAP я все же дал, надеюсь. Удачной работы!
ссылка на оригинал статьи http://habrahabr.ru/post/174109/
Добавить комментарий