C++ 调用 SOAP Web Service

C++ 调用 SOAP Web Service

2019/08/14:这篇文章已通过时了。Webcc 已经演化成了一个比较完备的纯 HTTP 程序库。html

背景

首先,gSoap 确定是个不错的选择,可是若是你的程序要调用多个 Web Services(即有多个 WSDL),gSoap 会比较麻烦。还有一个问题就是,gSoap 从 WSDL 自动生成的代码实在是太难用了。固然,这些都不是什么问题,真在的问题是许可证(License),gSoap 用在商业产品中是要收费的。java

公司比较穷,舍不得花钱买 gSoap,可是 C++ 调 Web Service 还真没什么好办法。尝试了五六个半死不活的库后,最终锁定了 WWSAPI(Windows Web Services API)。git

WWSAPI 的官方文档常常让人摸不着头脑,没有完整的示例,给出一段代码,经常须要几经调整才能使用。WWSAPI 自动生成的代码,是纯 C 的接口,在难用程度上,较 gSoap 有过之而无不及。在消息参数上,它强制使用双字节 Unicode,咱们的输入输出都是 UTF8 的 std::string,因而莫名地多出不少编码转换。WWSAPI 须要你手动分配堆(heap),须要你指定消息的缓冲大小,而最严重的问题是,它不够稳定,特别是在子线程里调用时,莫名其妙链接就会断掉。github

因而,我就动手本身写了个 webcc
一开始 webcc 只支持 SOAP,名字就叫 csoap,后来支持了 REST,因而更名为 webcc,取 Web C++ 的意思。web

原理

Webcc 没有提供从 WSDL 自动生成代码的功能,一来是由于这一过程太复杂了,二来是自动生成的代码通常都很差用。因此 webcc 最好搭配 SoapUI 一块儿使用。SoapUI 能够帮助咱们为每个 Web Service 操做(operation)生成请求的样例,基于请求样例,就很容易发起调用了,也避免了直接阅读 WSDL。windows

下面以 ParaSoft 提供的 Calculator 为例,首先下载 WSDL,而后在 SoapUI 里建立一个 SOAP 项目,记得勾上 "Create sample requests for all operations?" 这个选项,而后就能看到下面这样的请求样例了:ide

<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>

这个操做是 add,有两个参数:xy。此外值得注意的还有 XML namespace,好比 xmlns:cal="http://www.parasoft.com/wsdl/calculator/"函数

要调用这个 add 操做,只要发一个 HTTP 请求,并把上面这个 SOAP Envelope 做为请求的 Content。在 SoapUI 里把 Request 切换到 “Raw" 模式,就能够看到下面这样完整的 HTTP 请求:ui

POST http://ws1.parasoft.com/glue/calculator HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: text/xml;charset=UTF-8
SOAPAction: "add"
Content-Length: 300
Host: ws1.parasoft.com
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

<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>1</cal:y>
      </cal:add>
   </soapenv:Body>
</soapenv:Envelope>

因此 webcc 所作的,只不过是跟 ws1.parasoft.com 创建 TCP Socket 链接,而后发送上面这段内容而已。编码

用法

首先,建立一个类 CalcClient,继承自 webcc::SoapClient

#include <string>
#include "webcc/soap_client.h"

class CalcClient : public webcc::SoapClient {
public:
  CalcClient() {
    Init();
  }

Init() 函数里,初始化 URL、host、port 等等:

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";
  }

因为四个计算器操做(add, subtract, multiplydivide)都一致的具备两个参数,咱们能够稍微封装一下,弄一个辅助函数叫 Calc

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;
}

值得注意的是,做为局部变量的参数(parameters),利用了 C++11 的 Move 语义,避免了额外的拷贝开销。
当参数为很长的字符串时(好比 XML string),这一点特别有用。

最后,四个操做就是简单的转调 Calc 而已:

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);
}

局限

固然,webcc 有不少局限,好比:

  • 只支持 int, double, boolstring 这几种参数类型;
  • 只支持 UTF-8 编码的消息内容;
  • 一次调用一个链接;
  • 链接是同步(阻塞)模式,能够指定 timeout(缺省为 15s)。

依赖

在实现上,webcc 有下面这些依赖:

  • Boost 1.66+;
  • XML 解析和构造基于 pugixml;
  • 构建系统是 CMake,应该能够很方便地集成到其余 C++ 项目中。
相关文章
相关标签/搜索