Update README and tutorials.
parent
5bc988b093
commit
63e9e87f4f
@ -1,214 +1,21 @@
|
||||
# csoap
|
||||
# webcc
|
||||
|
||||
A lightweight C++ SOAP client & server library based on Boost.Asio.
|
||||
A lightweight C++ REST and SOAP client and server library based on Boost.Asio.
|
||||
|
||||
[中文教程](doc/ClientTutorial_zh-CN.md)
|
||||
## Tutorials
|
||||
|
||||
## Client Usage
|
||||
**SOAP:**
|
||||
- [SOAP Client Tutorial](doc/SoapClientTutorial.md)
|
||||
- [SOAP Server Tutorial](doc/SoapServerTutorial.md)
|
||||
|
||||
Firstly, please install **SoapUI** if you don't have it. We need SoapUI to generate sample requests for each web service operation. The open source version is good enough.
|
||||
- [SOAP 客户端教程](doc/SoapClientTutorial_zh-CN.md)
|
||||
|
||||
Take the calculator web service provided by ParaSoft as an example. Download the WSDL from http://ws1.parasoft.com/glue/calculator.wsdl, create a SOAP project within SoapUI (remember to check "**Create sample requests for all operations?**"), you will see the sample request for "add" operation as the following:
|
||||
```xml
|
||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:cal="http://www.parasoft.com/wsdl/calculator/">
|
||||
<soapenv:Header/>
|
||||
<soapenv:Body>
|
||||
<cal:add>
|
||||
<cal:x>1</cal:x>
|
||||
<cal:y>2</cal:y>
|
||||
</cal:add>
|
||||
</soapenv:Body>
|
||||
</soapenv:Envelope>
|
||||
```
|
||||
|
||||
In order to call the "add" operation, we have to send a HTTP request with the above SOAP envelope as the content. Let's see how to do this with *csoap*.
|
||||
|
||||
Firstly, create a class `CalculatorClient` which is derived from `csoap::SoapClient`:
|
||||
|
||||
```cpp
|
||||
#include <string>
|
||||
#include "csoap/soap_client.h"
|
||||
|
||||
class CalculatorClient : public csoap::SoapClient {
|
||||
public:
|
||||
CalculatorClient() {
|
||||
Init();
|
||||
}
|
||||
```
|
||||
|
||||
Initialize the URL, host, port, etc. in `Init()`:
|
||||
```cpp
|
||||
private:
|
||||
void Init() {
|
||||
url_ = "/glue/calculator";
|
||||
host_ = "ws1.parasoft.com";
|
||||
port_ = ""; // Default to "80".
|
||||
service_ns_ = { "cal", "http://www.parasoft.com/wsdl/calculator/" };
|
||||
result_name_ = "Result";
|
||||
}
|
||||
```
|
||||
|
||||
Because four calculator operations (*add*, *subtract*, *multiply* and *divide*) all have two parameters, we create a wrapper for `SoapClient::Call()`, name is as `Calc`:
|
||||
```cpp
|
||||
bool Calc(const std::string& operation,
|
||||
const std::string& x_name,
|
||||
const std::string& y_name,
|
||||
double x,
|
||||
double y,
|
||||
double* result) {
|
||||
// Prepare parameters.
|
||||
std::vector<csoap::Parameter> parameters{
|
||||
{ x_name, x },
|
||||
{ y_name, y }
|
||||
};
|
||||
|
||||
// Make the call.
|
||||
std::string result_str;
|
||||
csoap::Error error = Call(operation, std::move(parameters), &result_str);
|
||||
|
||||
// Error handling if any.
|
||||
if (error != csoap::kNoError) {
|
||||
std::cerr << "Error: " << error;
|
||||
std::cerr << ", " << csoap::GetErrorMessage(error) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert the result from string to double.
|
||||
try {
|
||||
*result = boost::lexical_cast<double>(result_str);
|
||||
} catch (boost::bad_lexical_cast&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
Note that the local parameters are moved (with `std::move()`). This is to avoid expensive copy of long string parameters, e.g., XML strings.
|
||||
|
||||
Finally, we implement the four operations simply as the following:
|
||||
```cpp
|
||||
bool Add(double x, double y, double* result) {
|
||||
return Calc("add", "x", "y", x, y, result);
|
||||
}
|
||||
|
||||
bool Subtract(double x, double y, double* result) {
|
||||
return Calc("subtract", "x", "y", x, y, result);
|
||||
}
|
||||
|
||||
bool Multiply(double x, double y, double* result) {
|
||||
return Calc("multiply", "x", "y", x, y, result);
|
||||
}
|
||||
|
||||
bool Divide(double x, double y, double* result) {
|
||||
return Calc("divide", "numerator", "denominator", x, y, result);
|
||||
}
|
||||
```
|
||||
See? It's not that complicated. Check folder ***demo/calculator_client*** for the full example.
|
||||
|
||||
## Server Usage
|
||||
|
||||
*NOTE: The server part is currently under development, not stable enough to be used in a real production.*
|
||||
|
||||
Suppose you want to provide a calculator web service just like the one from ParaSoft.
|
||||
|
||||
Firstly, create a class `CalculatorService` which is derived from `csoap::SoapService`, override the `Handle` method:
|
||||
```cpp
|
||||
#include "csoap/soap_service.h"
|
||||
|
||||
class CalculatorService : public csoap::SoapService {
|
||||
public:
|
||||
CalculatorService();
|
||||
|
||||
bool Handle(const csoap::SoapRequest& soap_request,
|
||||
csoap::SoapResponse* soap_response) override;
|
||||
};
|
||||
```
|
||||
The `Handle` method has two parameters, one for request (input), one for response (output).
|
||||
The implementation is quite straightforward:
|
||||
- Get operation from request.
|
||||
- Get parameters from request.
|
||||
- Calculate the result.
|
||||
- Set namespaces, result name and so on to response.
|
||||
- Set result to response.
|
||||
|
||||
```cpp
|
||||
#include "calculator_service.h"
|
||||
|
||||
#include "boost/lexical_cast.hpp"
|
||||
|
||||
#include "csoap/soap_request.h"
|
||||
#include "csoap/soap_response.h"
|
||||
|
||||
CalculatorService::CalculatorService() {
|
||||
}
|
||||
|
||||
bool CalculatorService::Handle(const csoap::SoapRequest& soap_request,
|
||||
csoap::SoapResponse* soap_response) {
|
||||
try {
|
||||
if (soap_request.operation() == "add") {
|
||||
double x = boost::lexical_cast<double>(soap_request.GetParameter("x"));
|
||||
double y = boost::lexical_cast<double>(soap_request.GetParameter("y"));
|
||||
|
||||
double result = x + y;
|
||||
|
||||
soap_response->set_soapenv_ns(csoap::kSoapEnvNamespace);
|
||||
soap_response->set_service_ns({ "cal", "http://mycalculator/" });
|
||||
soap_response->set_operation(soap_request.operation());
|
||||
soap_response->set_result_name("Result");
|
||||
soap_response->set_result(boost::lexical_cast<std::string>(result));
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
// NOT_IMPLEMENTED
|
||||
}
|
||||
} catch (boost::bad_lexical_cast&) {
|
||||
// BAD_REQUEST
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
The `main` function would be:
|
||||
```cpp
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc != 2) {
|
||||
std::cout << "Usage: " << argv[0] << " <port>" << std::endl;
|
||||
std::cout << " E.g.," << std::endl;
|
||||
std::cout << " " << argv[0] << " 8080" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* host = "0.0.0.0"; // TODO
|
||||
|
||||
try {
|
||||
csoap::HttpServer server(host, argv[1]);
|
||||
|
||||
csoap::SoapServicePtr service(new CalculatorService);
|
||||
server.RegisterService(service);
|
||||
|
||||
server.Run();
|
||||
|
||||
} catch (std::exception& e) {
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Limitations
|
||||
## Dependencies
|
||||
|
||||
- Only support `int`, `double`, `bool` and `string` parameters.
|
||||
- Only support UTF-8 encoded content.
|
||||
- One connection one call.
|
||||
- Connection is in synchronous (or blocking) mode; timeout (default to 30s) is configurable.
|
||||
- C++11
|
||||
- Boost 1.66+
|
||||
- pugixml (already included in the source tree) (SOAP only)
|
||||
|
||||
## Dependencies
|
||||
## Build
|
||||
|
||||
- Boost 1.66+.
|
||||
- XML processing is based on PugiXml, which is already included in the source tree.
|
||||
- Build system is CMake, but it should be quite easy to integrate into your own project.
|
||||
TODO
|
||||
|
@ -0,0 +1,101 @@
|
||||
# SOAP Client Tutorial
|
||||
|
||||
Firstly, please install **SoapUI** if you don't have it. We need SoapUI to generate sample requests for each web service operation. The open source version is good enough.
|
||||
|
||||
Take the calculator web service provided by ParaSoft as an example. Download the WSDL from http://ws1.parasoft.com/glue/calculator.wsdl, create a SOAP project within SoapUI (remember to check "**Create sample requests for all operations?**"), you will see the sample request for "add" operation as the following:
|
||||
```xml
|
||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:cal="http://www.parasoft.com/wsdl/calculator/">
|
||||
<soapenv:Header/>
|
||||
<soapenv:Body>
|
||||
<cal:add>
|
||||
<cal:x>1</cal:x>
|
||||
<cal:y>2</cal:y>
|
||||
</cal:add>
|
||||
</soapenv:Body>
|
||||
</soapenv:Envelope>
|
||||
```
|
||||
|
||||
In order to call the "add" operation, we have to send a HTTP request with the above SOAP envelope as the content. Let's see how to do this with *webcc*.
|
||||
|
||||
Firstly, create a class `CalcClient` which is derived from `webcc::SoapClient`:
|
||||
|
||||
```cpp
|
||||
#include <string>
|
||||
#include "webcc/soap_client.h"
|
||||
|
||||
class CalcClient : public webcc::SoapClient {
|
||||
public:
|
||||
CalcClient() {
|
||||
Init();
|
||||
}
|
||||
```
|
||||
|
||||
Initialize the URL, host, port, etc. in `Init()`:
|
||||
```cpp
|
||||
private:
|
||||
void Init() {
|
||||
url_ = "/glue/calculator";
|
||||
host_ = "ws1.parasoft.com";
|
||||
port_ = ""; // Default to "80".
|
||||
service_ns_ = { "cal", "http://www.parasoft.com/wsdl/calculator/" };
|
||||
result_name_ = "Result";
|
||||
}
|
||||
```
|
||||
|
||||
Because four calculator operations (*add*, *subtract*, *multiply* and *divide*) all have two parameters, we create a wrapper for `SoapClient::Call()`, name is as `Calc`:
|
||||
```cpp
|
||||
bool Calc(const std::string& operation,
|
||||
const std::string& x_name,
|
||||
const std::string& y_name,
|
||||
double x,
|
||||
double y,
|
||||
double* result) {
|
||||
// Prepare parameters.
|
||||
std::vector<webcc::Parameter> parameters{
|
||||
{ x_name, x },
|
||||
{ y_name, y }
|
||||
};
|
||||
|
||||
// Make the call.
|
||||
std::string result_str;
|
||||
webcc::Error error = Call(operation, std::move(parameters), &result_str);
|
||||
|
||||
// Error handling if any.
|
||||
if (error != webcc::kNoError) {
|
||||
std::cerr << "Error: " << error;
|
||||
std::cerr << ", " << webcc::GetErrorMessage(error) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert the result from string to double.
|
||||
try {
|
||||
*result = boost::lexical_cast<double>(result_str);
|
||||
} catch (boost::bad_lexical_cast&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
Note that the local parameters are moved (with `std::move()`). This is to avoid expensive copy of long string parameters, e.g., XML strings.
|
||||
|
||||
Finally, we implement the four operations simply as the following:
|
||||
```cpp
|
||||
bool Add(double x, double y, double* result) {
|
||||
return Calc("add", "x", "y", x, y, result);
|
||||
}
|
||||
|
||||
bool Subtract(double x, double y, double* result) {
|
||||
return Calc("subtract", "x", "y", x, y, result);
|
||||
}
|
||||
|
||||
bool Multiply(double x, double y, double* result) {
|
||||
return Calc("multiply", "x", "y", x, y, result);
|
||||
}
|
||||
|
||||
bool Divide(double x, double y, double* result) {
|
||||
return Calc("divide", "numerator", "denominator", x, y, result);
|
||||
}
|
||||
```
|
||||
See? It's not that complicated. Check folder ***demo/calculator_client*** for the full example.
|
@ -0,0 +1,94 @@
|
||||
# SOAP Server Tutorial
|
||||
|
||||
Suppose you want to provide a calculator web service just like the one from [ParaSoft](http://ws1.parasoft.com/glue/calculator.wsdl).
|
||||
|
||||
Firstly, create a class `CalcService` which is derived from `webcc::SoapService`, override the `Handle` method:
|
||||
```cpp
|
||||
#include "webcc/soap_service.h"
|
||||
|
||||
class CalcService : public webcc::SoapService {
|
||||
public:
|
||||
CalcService() = default;
|
||||
|
||||
bool Handle(const webcc::SoapRequest& soap_request,
|
||||
webcc::SoapResponse* soap_response) override;
|
||||
};
|
||||
```
|
||||
The `Handle` method has two parameters, one for request (input), one for response (output).
|
||||
The implementation is quite straightforward:
|
||||
- Get operation from request.
|
||||
- Get parameters from request.
|
||||
- Calculate the result.
|
||||
- Set namespaces, result name and so on to response.
|
||||
- Set result to response.
|
||||
|
||||
```cpp
|
||||
#include "calc_service.h"
|
||||
|
||||
#include "boost/lexical_cast.hpp"
|
||||
|
||||
#include "webcc/soap_request.h"
|
||||
#include "webcc/soap_response.h"
|
||||
|
||||
bool CalcService::Handle(const webcc::SoapRequest& soap_request,
|
||||
webcc::SoapResponse* soap_response) {
|
||||
try {
|
||||
if (soap_request.operation() == "add") {
|
||||
double x = boost::lexical_cast<double>(soap_request.GetParameter("x"));
|
||||
double y = boost::lexical_cast<double>(soap_request.GetParameter("y"));
|
||||
|
||||
double result = x + y;
|
||||
|
||||
soap_response->set_soapenv_ns(webcc::kSoapEnvNamespace);
|
||||
soap_response->set_service_ns({
|
||||
"cal",
|
||||
"http://www.example.com/calculator/"
|
||||
});
|
||||
soap_response->set_operation(soap_request.operation());
|
||||
soap_response->set_result_name("Result");
|
||||
soap_response->set_result(std::to_string(result));
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
// NOT_IMPLEMENTED
|
||||
}
|
||||
} catch (boost::bad_lexical_cast&) {
|
||||
// BAD_REQUEST
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
The `main` function would be:
|
||||
```cpp
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc != 2) {
|
||||
std::cout << "Usage: " << argv[0] << " <port>" << std::endl;
|
||||
std::cout << " E.g.," << std::endl;
|
||||
std::cout << " " << argv[0] << " 8080" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned short port = std::atoi(argv[1]);
|
||||
|
||||
// Number of worker threads.
|
||||
std::size_t workers = 2;
|
||||
|
||||
try {
|
||||
webcc::SoapServer server(port, workers);
|
||||
|
||||
server.RegisterService(std::make_shared<CalcService>(),
|
||||
"/calculator");
|
||||
|
||||
server.Run();
|
||||
|
||||
} catch (std::exception& e) {
|
||||
std::cerr << "exception: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
Loading…
Reference in New Issue