It so happened that work with gSOAP on the habr is described very poorly. Just
one post , to be honest. But it describes the creation of a web service, but what about the client applications? Not long ago, I was faced with the task of organizing work with a remote server using SOAP - and I decided to write a short article about it.
Since I cannot provide the wsdl files I work with (NDA and all that), then I started to search for services suitable for testing. Two seemed interesting to me:
http://www.webservicex.net/ValidateEmail.asmx?WSDLhttp://www.webservicex.net/country.asmx?WSDLI never found where to download the wsdl files, so I copied their contents and saved them under the names ValidateEmail.wsdl and country.wsdl
Download gSOAP here -
http://www.cs.fsu.edu/~engelen/soap.html . At the same address you can read about gSOAP.
The latest version as of this writing is 2.8.14
')
Let's get to work. Two very important utilities live in the gsoap \ bin \ win32 \ folder. At first we are interested in wsdl2h.exe. You can learn more about it using the help:
> wsdlh2.exe -hRun it with the following parameters:
wsdl2h.exe -o emailAndCountry.h ValidateEmail.wsdl country.wsdlIt's pretty simple, we only specified the name of the output file and the list of wsdl files we want to work with.
After that, we directly generate the code of C ++ classes:
soapcpp2.exe -C -dgSoap -j -L -x -I "ADDRESS_TO_GSOAP \ gsoap-2.8 \ gsoap \ import" emailAndCountry.hThe -C switch says to generate only client code.
-dgSoap asks to put all the files in the gSoap folder (you need to create it first). We do not generate lib files and do not inherit from the soap structure; the -x switch asks not to generate XML files with sample messages. We specify the address to the folder with gSOAP and the file that we will parse and on the basis of which we generate the code.
gSOAP of different versions can generate different code (which is logical, in general), and even the composition of the files will differ. This is important to remember if you suddenly want to generate gSOAP files on the build server, and not store them in the version control system.
After all the manipulations in the gSoap folder we see a lot of new files. These are plus -h and -cpp files, as well as countrySoap.nsmap and ValidateEmailSoap.nsmap. They match, you can save their contents in one (for example, namespaces.nsmap), and delete them. namespaces.nsmap must be injected into the project. As a rule, this is done in some auxiliary class that will work with gSOAP. Yes, such a class will certainly exist.
After that we add stdsoap2.h and stdsoap2.cpp to the gSoap folder - they are included in soapStub.h
Add the entire folder to the project and start working :)
We look at the classes soapcountrySoapProxy.cpp and soapValidateEmailSoapProxy.cpp; in * _init methods (soap_mode imode, soap_mode omode) - we delete namespases (we knowingly include our namespaces.nsmap).
Let's start with the e-mail validation capability - gSoap / soapValidateEmailSoapProxy.h
We are interested in the method
virtual int IsValidEmail (_ns1__IsValidEmail * ns1__IsValidEmail, _ns1__IsValidEmailResponse * ns1__IsValidEmailResponse)
See the description of the arguments of this function:
class SOAP_CMAC _ns1__IsValidEmail { public: std::string *Email; struct soap *soap;
Field * Email, we certainly come in handy.
Create an object of this class and specify the object that will be checked.
_ns1__IsValidEmail isValidEmailRequest; std::string CHECKED_E_MAIL("pisem@sovsem.net"); isValidEmailRequest.Email = &CHECKED_E_MAIL;
Immediately create an object to which the result will come:
_ns1__IsValidEmailResponse isValidEmailResponse;
We see that the result will be in the bool IsValidEmailResult field.
We send a request to the server:
const int gSoapResult = validateEmailProxy.IsValidEmail(&isValidEmailRequest, &isValidEmailResponse);
With a sniffer we look exactly what we sent:
<?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>
We look at the answer - we see that the result field is false. Sniffer confirms this.
We try another address - press@fsb.ru
Immediately, I note that the server thinks for quite a long time, but this is hardly connected with the address being checked.
But the answer still comes, and this address is considered invalid in our country.
Well, we try the obviously “bad” address - “1354 @”
The execution time is eternity. Works really very long.
After about a minute I was tired of waiting, and I decided to try again to check the valid address - adv@thematicmedia.ru
Ok, the result is false.
adv @ thematicmedia - false
adv @ l - false
I tried one of my ancient mailboxes - the server started thinking again. For a long time. But still answered - false.
Well, it turns out that the problem is only in the server, our server sends the answers to the server correctly. The code works, but is absolutely useless.
We try the second class - countrySoapProxy
The number of his methods is much more, they can be viewed in the header.
We will test them in order, starting with 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; }
Answer received:
<NewDataSet> <Table> <countrycode>gb</countrycode> <name>Great Britain</name> </Table> <Table> <countrycode>gb</countrycode> <name>Great Britain</name> </Table> </NewDataSet>
I confess I was expecting that the answer would be only a country, not a healthy DataSet, well, yes, this is nagging. Country with the code RU this service also knows, cheers!
Go to another method:
_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);
And this method works as it should:
<NewDataSet> <Table> <code>7</code> <name>Russian Federation</name> </Table> <Table> <code>7</code> <name>Russian Federation</name> </Table> </NewDataSet>
Well, for this service, everything works, as expected, for training purposes it fits a little more than the first. At this experiment, you can still stop.
This approach works great when we are going to work with only one service, which is located at a given address and does not require authorization.
But we can deploy a web service anywhere and in any way!
In fact, gSoap can do a lot. For example, you can set a login password through a soap structure. An example of the simplest basic authorization:
soap.userid = login; soap.passwd =password;
The description of the soap structure is in stdsoap2.h:
const char *userid; const char *passwd;
You can change the address to which requests will be sent in two ways: set it at the request itself, or set when creating a SOAP proxy.
All this is quite obvious from the leaders themselves:
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);
In the case of the second method, we need to create an object like this:
countrySoapProxy countryProxy("http://www.webservicex.net/country.asmx");
Nothing too complicated, is it? Here you can set a normal IP address with a port.
I also did not take into account how to set names themselves. This approach will allow you to receive not ns1__IsValidEmail as class names, but something like email__IsValidEmail. When there are many classes, you can get confused. Probably. This is also done without problems. Create a typeMap.dat file with the contents of the following format:
myCustomNamespace = "
www.webservicex.net "
Those. everything is simple: we specify the name of namespace and its address.
In general, working with gSoap is not particularly difficult. But there are a few things that come up when working with him. For example, when specifying a login and password, they may change after the execution of the request. Those. The code might look something like this:
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);
For some reason, this looks doubtful.
Perhaps I didn’t take something into account, but a brief experience with gSOAP leaves a mixed impression: high development speed, a relatively small amount of code - all this is a plus. But here is a code jumble, a mixture of C and C ++ is a minus. But I admit that this is not a minus for everyone, and there are much more advantages. In any case, I couldn’t find any decent alternatives to gSOAP - although I didn’t look very carefully, trusting the solution that was tested in our company.
This article is rather introductory, which briefly talks about getting started with gSOAP. I didn’t give different authorization methods here, I didn’t study inheritance issues from SOAP structures (is it necessary for something?). There are many unsolved questions, but I gave a brief digression to gSOAP, I hope. Have a good job!