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++ 項目中。
相關文章
相關標籤/搜索