Rework the parsing of soap response.
This commit is contained in:
@@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
#include "example/common/book_xml.h"
|
#include "example/common/book_xml.h"
|
||||||
|
|
||||||
|
static const std::string kResultName = "Result";
|
||||||
|
|
||||||
static void PrintSeparateLine() {
|
static void PrintSeparateLine() {
|
||||||
std::cout << "--------------------------------";
|
std::cout << "--------------------------------";
|
||||||
std::cout << "--------------------------------";
|
std::cout << "--------------------------------";
|
||||||
@@ -16,7 +18,6 @@ BookClient::BookClient(const std::string& host, const std::string& port)
|
|||||||
: soap_client_(host, port), code_(0) {
|
: soap_client_(host, port), code_(0) {
|
||||||
soap_client_.set_url("/book");
|
soap_client_.set_url("/book");
|
||||||
soap_client_.set_service_ns({ "ser", "http://www.example.com/book/" });
|
soap_client_.set_service_ns({ "ser", "http://www.example.com/book/" });
|
||||||
soap_client_.set_result_name("Result");
|
|
||||||
|
|
||||||
// Customize response XML format.
|
// Customize response XML format.
|
||||||
soap_client_.set_format_raw(false);
|
soap_client_.set_format_raw(false);
|
||||||
@@ -91,7 +92,6 @@ bool BookClient::Call0(const std::string& operation, std::string* result_str) {
|
|||||||
return Call(operation, {}, result_str);
|
return Call(operation, {}, result_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool BookClient::Call1(const std::string& operation,
|
bool BookClient::Call1(const std::string& operation,
|
||||||
webcc::SoapParameter&& parameter,
|
webcc::SoapParameter&& parameter,
|
||||||
std::string* result_str) {
|
std::string* result_str) {
|
||||||
@@ -104,7 +104,8 @@ bool BookClient::Call1(const std::string& operation,
|
|||||||
bool BookClient::Call(const std::string& operation,
|
bool BookClient::Call(const std::string& operation,
|
||||||
std::vector<webcc::SoapParameter>&& parameters,
|
std::vector<webcc::SoapParameter>&& parameters,
|
||||||
std::string* result_str) {
|
std::string* result_str) {
|
||||||
if (!soap_client_.Request(operation, std::move(parameters), result_str)) {
|
if (!soap_client_.Request(operation, std::move(parameters), kResultName,
|
||||||
|
result_str)) {
|
||||||
PrintError();
|
PrintError();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ bool BookService::CreateBook(const webcc::SoapRequest& soap_request,
|
|||||||
std::string id = g_book_store.AddBook(book);
|
std::string id = g_book_store.AddBook(book);
|
||||||
|
|
||||||
std::string response_xml = NewResultXml(0, "ok", "book", "id",
|
std::string response_xml = NewResultXml(0, "ok", "book", "id",
|
||||||
id.c_str());
|
id.c_str());
|
||||||
|
|
||||||
soap_response->set_result_moved(std::move(response_xml), true);
|
soap_response->set_result_moved(std::move(response_xml), true);
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static const std::string kResultName = "Result";
|
||||||
|
|
||||||
class CalcClient {
|
class CalcClient {
|
||||||
public:
|
public:
|
||||||
CalcClient(const std::string& host, const std::string& port)
|
CalcClient(const std::string& host, const std::string& port)
|
||||||
@@ -15,7 +17,6 @@ class CalcClient {
|
|||||||
soap_client_.set_service_ns({
|
soap_client_.set_service_ns({
|
||||||
"ser", "http://www.example.com/calculator/"
|
"ser", "http://www.example.com/calculator/"
|
||||||
});
|
});
|
||||||
soap_client_.set_result_name("Result");
|
|
||||||
|
|
||||||
// Customize request XML format.
|
// Customize request XML format.
|
||||||
soap_client_.set_format_raw(false);
|
soap_client_.set_format_raw(false);
|
||||||
@@ -54,7 +55,8 @@ class CalcClient {
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::string result_str;
|
std::string result_str;
|
||||||
if (!soap_client_.Request(operation, std::move(parameters), &result_str)) {
|
if (!soap_client_.Request(operation, std::move(parameters), kResultName,
|
||||||
|
&result_str)) {
|
||||||
PrintError();
|
PrintError();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static const std::string kResultName = "Result";
|
||||||
|
|
||||||
class CalcClient {
|
class CalcClient {
|
||||||
public:
|
public:
|
||||||
// NOTE: Parasoft's calculator service uses SOAP V1.1.
|
// NOTE: Parasoft's calculator service uses SOAP V1.1.
|
||||||
@@ -16,7 +18,6 @@ class CalcClient {
|
|||||||
soap_client_.set_service_ns({
|
soap_client_.set_service_ns({
|
||||||
"cal", "http://www.parasoft.com/wsdl/calculator/"
|
"cal", "http://www.parasoft.com/wsdl/calculator/"
|
||||||
});
|
});
|
||||||
soap_client_.set_result_name("Result");
|
|
||||||
|
|
||||||
// Customize request XML format.
|
// Customize request XML format.
|
||||||
soap_client_.set_format_raw(false);
|
soap_client_.set_format_raw(false);
|
||||||
@@ -39,6 +40,11 @@ class CalcClient {
|
|||||||
return Calc("divide", "numerator", "denominator", x, y, result);
|
return Calc("divide", "numerator", "denominator", x, y, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only for testing purpose.
|
||||||
|
bool Unknown(double x, double y, double* result) {
|
||||||
|
return Calc("unknown", "x", "y", x, y, result);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool Calc(const std::string& operation,
|
bool Calc(const std::string& operation,
|
||||||
const std::string& x_name, const std::string& y_name,
|
const std::string& x_name, const std::string& y_name,
|
||||||
@@ -50,7 +56,8 @@ class CalcClient {
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::string result_str;
|
std::string result_str;
|
||||||
if (!soap_client_.Request(operation, std::move(parameters), &result_str)) {
|
if (!soap_client_.Request(operation, std::move(parameters), kResultName,
|
||||||
|
&result_str)) {
|
||||||
PrintError();
|
PrintError();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -69,7 +76,12 @@ class CalcClient {
|
|||||||
if (soap_client_.timed_out()) {
|
if (soap_client_.timed_out()) {
|
||||||
std::cout << " (timed out)";
|
std::cout << " (timed out)";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
if (soap_client_.fault()) {
|
||||||
|
std::cout << *soap_client_.fault() << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
webcc::SoapClient soap_client_;
|
webcc::SoapClient soap_client_;
|
||||||
@@ -102,5 +114,7 @@ int main() {
|
|||||||
printf("divide: %.1f\n", result);
|
printf("divide: %.1f\n", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
calc.Unknown(x, y, &result);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ const char* DescribeError(Error error) {
|
|||||||
return "Socket write error";
|
return "Socket write error";
|
||||||
case kHttpError:
|
case kHttpError:
|
||||||
return "HTTP error";
|
return "HTTP error";
|
||||||
|
case kServerError:
|
||||||
|
return "Server error";
|
||||||
case kXmlError:
|
case kXmlError:
|
||||||
return "XML error";
|
return "XML error";
|
||||||
default:
|
default:
|
||||||
|
|||||||
+14
-4
@@ -70,21 +70,31 @@ struct HttpStatus {
|
|||||||
kNotModified = 304,
|
kNotModified = 304,
|
||||||
kBadRequest = 400,
|
kBadRequest = 400,
|
||||||
kNotFound = 404,
|
kNotFound = 404,
|
||||||
InternalServerError = 500,
|
kInternalServerError = 500,
|
||||||
kNotImplemented = 501,
|
kNotImplemented = 501,
|
||||||
kServiceUnavailable = 503,
|
kServiceUnavailable = 503,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Error codes.
|
// Client side error codes.
|
||||||
enum Error {
|
enum Error {
|
||||||
kNoError = 0,
|
kNoError = 0, // i.e., OK
|
||||||
|
|
||||||
kHostResolveError,
|
kHostResolveError,
|
||||||
kEndpointConnectError,
|
kEndpointConnectError,
|
||||||
kHandshakeError,
|
kHandshakeError, // HTTPS handshake
|
||||||
kSocketReadError,
|
kSocketReadError,
|
||||||
kSocketWriteError,
|
kSocketWriteError,
|
||||||
|
|
||||||
|
// HTTP error.
|
||||||
|
// E.g., failed to parse HTTP response (invalid content length, etc.).
|
||||||
kHttpError,
|
kHttpError,
|
||||||
|
|
||||||
|
// Server error.
|
||||||
|
// E.g., HTTP status 500 + SOAP Fault element.
|
||||||
|
kServerError,
|
||||||
|
|
||||||
|
// XML parsing error.
|
||||||
kXmlError,
|
kXmlError,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ const std::string& ToString(int status) {
|
|||||||
case HttpStatus::kNotFound:
|
case HttpStatus::kNotFound:
|
||||||
return NOT_FOUND;
|
return NOT_FOUND;
|
||||||
|
|
||||||
case HttpStatus::InternalServerError:
|
case HttpStatus::kInternalServerError:
|
||||||
return INTERNAL_SERVER_ERROR;
|
return INTERNAL_SERVER_ERROR;
|
||||||
|
|
||||||
case HttpStatus::kNotImplemented:
|
case HttpStatus::kNotImplemented:
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ void SoapAsyncClient::ResponseHandler(SoapResponseHandler soap_response_handler,
|
|||||||
if (!soap_response.FromXml(http_response->content())) {
|
if (!soap_response.FromXml(http_response->content())) {
|
||||||
soap_response_handler("", kXmlError, false);
|
soap_response_handler("", kXmlError, false);
|
||||||
} else {
|
} else {
|
||||||
soap_response_handler(soap_response.result_moved(), kNoError, false);
|
//soap_response_handler(soap_response.result_moved(), kNoError, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-6
@@ -3,6 +3,8 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <utility> // for move()
|
#include <utility> // for move()
|
||||||
|
|
||||||
|
#include "boost/algorithm/string.hpp"
|
||||||
|
|
||||||
#include "webcc/soap_request.h"
|
#include "webcc/soap_request.h"
|
||||||
#include "webcc/soap_response.h"
|
#include "webcc/soap_response.h"
|
||||||
|
|
||||||
@@ -24,12 +26,12 @@ SoapClient::SoapClient(const std::string& host, const std::string& port,
|
|||||||
|
|
||||||
bool SoapClient::Request(const std::string& operation,
|
bool SoapClient::Request(const std::string& operation,
|
||||||
std::vector<SoapParameter>&& parameters,
|
std::vector<SoapParameter>&& parameters,
|
||||||
std::string* result) {
|
SoapResponse::Parser parser) {
|
||||||
assert(service_ns_.IsValid());
|
assert(service_ns_.IsValid());
|
||||||
assert(!url_.empty() && !host_.empty());
|
assert(!url_.empty() && !host_.empty());
|
||||||
assert(!result_name_.empty());
|
|
||||||
|
|
||||||
error_ = kNoError;
|
error_ = kNoError;
|
||||||
|
fault_.reset();
|
||||||
|
|
||||||
SoapRequest soap_request;
|
SoapRequest soap_request;
|
||||||
|
|
||||||
@@ -73,16 +75,35 @@ bool SoapClient::Request(const std::string& operation,
|
|||||||
}
|
}
|
||||||
|
|
||||||
SoapResponse soap_response;
|
SoapResponse soap_response;
|
||||||
soap_response.set_result_name(result_name_);
|
soap_response.set_operation(operation);
|
||||||
|
soap_response.set_parser(parser);
|
||||||
|
|
||||||
if (!soap_response.FromXml(http_client_.response()->content())) {
|
if (!soap_response.FromXml(http_client_.response()->content())) {
|
||||||
error_ = kXmlError;
|
if (soap_response.fault()) {
|
||||||
|
fault_ = soap_response.fault();
|
||||||
|
error_ = kServerError;
|
||||||
|
} else {
|
||||||
|
error_ = kXmlError;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
*result = soap_response.result_moved();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SoapClient::Request(const std::string& operation,
|
||||||
|
std::vector<SoapParameter>&& parameters,
|
||||||
|
const std::string& result_name,
|
||||||
|
std::string* result) {
|
||||||
|
auto parser = [result, &result_name](pugi::xml_node xnode) {
|
||||||
|
if (boost::iequals(soap_xml::GetNameNoPrefix(xnode), result_name)) {
|
||||||
|
soap_xml::GetText(xnode, result);
|
||||||
|
}
|
||||||
|
return false; // Stop next call.
|
||||||
|
};
|
||||||
|
|
||||||
|
return Request(operation, std::move(parameters), parser);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|||||||
+19
-8
@@ -7,6 +7,7 @@
|
|||||||
#include "webcc/http_client.h"
|
#include "webcc/http_client.h"
|
||||||
#include "webcc/soap_globals.h"
|
#include "webcc/soap_globals.h"
|
||||||
#include "webcc/soap_parameter.h"
|
#include "webcc/soap_parameter.h"
|
||||||
|
#include "webcc/soap_response.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
@@ -31,10 +32,6 @@ class SoapClient {
|
|||||||
service_ns_ = service_ns;
|
service_ns_ = service_ns;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_result_name(const std::string& result_name) {
|
|
||||||
result_name_ = result_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_format_raw(bool format_raw) {
|
void set_format_raw(bool format_raw) {
|
||||||
format_raw_ = format_raw;
|
format_raw_ = format_raw;
|
||||||
}
|
}
|
||||||
@@ -45,14 +42,30 @@ class SoapClient {
|
|||||||
|
|
||||||
bool Request(const std::string& operation,
|
bool Request(const std::string& operation,
|
||||||
std::vector<SoapParameter>&& parameters,
|
std::vector<SoapParameter>&& parameters,
|
||||||
|
SoapResponse::Parser parser);
|
||||||
|
|
||||||
|
// Shortcut for responses with single result node.
|
||||||
|
// The name of the single result node is specified by |result_name|.
|
||||||
|
// The text of the result node will be set to |result|.
|
||||||
|
bool Request(const std::string& operation,
|
||||||
|
std::vector<SoapParameter>&& parameters,
|
||||||
|
const std::string& result_name,
|
||||||
std::string* result);
|
std::string* result);
|
||||||
|
|
||||||
|
// HTTP status code (200, 500, etc.) in the response.
|
||||||
|
int http_status() const {
|
||||||
|
assert(http_client_.response());
|
||||||
|
return http_client_.response()->status();
|
||||||
|
}
|
||||||
|
|
||||||
bool timed_out() const {
|
bool timed_out() const {
|
||||||
return http_client_.timed_out();
|
return http_client_.timed_out();
|
||||||
}
|
}
|
||||||
|
|
||||||
Error error() const { return error_; }
|
Error error() const { return error_; }
|
||||||
|
|
||||||
|
std::shared_ptr<SoapFault> fault() const { return fault_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string host_;
|
std::string host_;
|
||||||
std::string port_; // Leave this empty to use default 80.
|
std::string port_; // Leave this empty to use default 80.
|
||||||
@@ -65,10 +78,6 @@ class SoapClient {
|
|||||||
// Namespace for your web service.
|
// Namespace for your web service.
|
||||||
SoapNamespace service_ns_;
|
SoapNamespace service_ns_;
|
||||||
|
|
||||||
// Response result XML node name.
|
|
||||||
// E.g., "Result".
|
|
||||||
std::string result_name_;
|
|
||||||
|
|
||||||
// Format request XML without any indentation or line breaks.
|
// Format request XML without any indentation or line breaks.
|
||||||
bool format_raw_;
|
bool format_raw_;
|
||||||
|
|
||||||
@@ -79,6 +88,8 @@ class SoapClient {
|
|||||||
HttpClient http_client_;
|
HttpClient http_client_;
|
||||||
|
|
||||||
Error error_;
|
Error error_;
|
||||||
|
|
||||||
|
std::shared_ptr<SoapFault> fault_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "webcc/soap_globals.h"
|
#include "webcc/soap_globals.h"
|
||||||
|
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
const std::string kSoapAction = "SOAPAction";
|
const std::string kSoapAction = "SOAPAction";
|
||||||
@@ -30,4 +32,13 @@ const SoapNamespace kSoapEnvNamespaceV12{
|
|||||||
"http://www.w3.org/2003/05/soap-envelope"
|
"http://www.w3.org/2003/05/soap-envelope"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const SoapFault& fault) {
|
||||||
|
os << "Fault: {" << std::endl
|
||||||
|
<< " faultcode: " << fault.faultcode << std::endl
|
||||||
|
<< " faultstring: " << fault.faultstring << std::endl
|
||||||
|
<< " detail: " << fault.detail << std::endl
|
||||||
|
<< "}";
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#ifndef WEBCC_SOAP_GLOBALS_H_
|
#ifndef WEBCC_SOAP_GLOBALS_H_
|
||||||
#define WEBCC_SOAP_GLOBALS_H_
|
#define WEBCC_SOAP_GLOBALS_H_
|
||||||
|
|
||||||
|
#include <iosfwd>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
@@ -35,6 +36,14 @@ struct SoapNamespace {
|
|||||||
extern const SoapNamespace kSoapEnvNamespaceV11;
|
extern const SoapNamespace kSoapEnvNamespaceV11;
|
||||||
extern const SoapNamespace kSoapEnvNamespaceV12;
|
extern const SoapNamespace kSoapEnvNamespaceV12;
|
||||||
|
|
||||||
|
struct SoapFault {
|
||||||
|
std::string faultcode;
|
||||||
|
std::string faultstring;
|
||||||
|
std::string detail;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const SoapFault& fault);
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|
||||||
#endif // WEBCC_SOAP_GLOBALS_H_
|
#endif // WEBCC_SOAP_GLOBALS_H_
|
||||||
|
|||||||
@@ -31,11 +31,11 @@ class SoapMessage {
|
|||||||
operation_ = operation;
|
operation_ = operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to SOAP request XML.
|
// Convert to SOAP XML.
|
||||||
void ToXml(bool format_raw, const std::string& indent,
|
void ToXml(bool format_raw, const std::string& indent,
|
||||||
std::string* xml_string);
|
std::string* xml_string);
|
||||||
|
|
||||||
// Parse from SOAP request XML.
|
// Parse from SOAP XML.
|
||||||
bool FromXml(const std::string& xml_string);
|
bool FromXml(const std::string& xml_string);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
+48
-12
@@ -7,6 +7,8 @@
|
|||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
void SoapResponse::ToXmlBody(pugi::xml_node xbody) {
|
void SoapResponse::ToXmlBody(pugi::xml_node xbody) {
|
||||||
|
assert(!result_name_.empty());
|
||||||
|
|
||||||
pugi::xml_node xop = soap_xml::AddChild(xbody, service_ns_.name,
|
pugi::xml_node xop = soap_xml::AddChild(xbody, service_ns_.name,
|
||||||
operation_ + "Response");
|
operation_ + "Response");
|
||||||
soap_xml::AddNSAttr(xop, service_ns_.name, service_ns_.url);
|
soap_xml::AddNSAttr(xop, service_ns_.name, service_ns_.url);
|
||||||
@@ -15,28 +17,62 @@ void SoapResponse::ToXmlBody(pugi::xml_node xbody) {
|
|||||||
result_name_);
|
result_name_);
|
||||||
|
|
||||||
// xresult.text().set() also works for PCDATA.
|
// xresult.text().set() also works for PCDATA.
|
||||||
|
// TODO: Add SetText() to soap_xml.h|cpp.
|
||||||
xresult.append_child(is_cdata_ ? pugi::node_cdata : pugi::node_pcdata)
|
xresult.append_child(is_cdata_ ? pugi::node_cdata : pugi::node_pcdata)
|
||||||
.set_value(result_.c_str());
|
.set_value(result_.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SoapResponse::FromXmlBody(pugi::xml_node xbody) {
|
bool SoapResponse::FromXmlBody(pugi::xml_node xbody) {
|
||||||
assert(!result_name_.empty());
|
// Check Fault element.
|
||||||
|
|
||||||
pugi::xml_node xresponse = xbody.first_child();
|
pugi::xml_node xfault = soap_xml::GetChildNoNS(xbody, "Fault");
|
||||||
if (xresponse) {
|
|
||||||
soap_xml::SplitName(xresponse, &service_ns_.name, nullptr);
|
|
||||||
service_ns_.url = soap_xml::GetNSAttr(xresponse, service_ns_.name);
|
|
||||||
|
|
||||||
pugi::xml_node xresult = soap_xml::GetChildNoNS(xresponse, result_name_);
|
// TODO: service_ns_.url
|
||||||
if (xresult) {
|
|
||||||
// The value of the first child node of type PCDATA/CDATA.
|
if (xfault) {
|
||||||
// xresult.text().get/as_string() also works.
|
fault_.reset(new SoapFault);
|
||||||
result_ = xresult.child_value();
|
|
||||||
return true;
|
pugi::xml_node xfaultcode = soap_xml::GetChildNoNS(xfault, "faultcode");
|
||||||
|
pugi::xml_node xfaultstring = soap_xml::GetChildNoNS(xfault, "faultstring");
|
||||||
|
pugi::xml_node xdetail = soap_xml::GetChildNoNS(xfault, "detail");
|
||||||
|
|
||||||
|
if (xfaultcode) {
|
||||||
|
fault_->faultcode = xfaultcode.text().as_string();
|
||||||
|
}
|
||||||
|
if (xfaultstring) {
|
||||||
|
fault_->faultstring = xfaultstring.text().as_string();
|
||||||
|
}
|
||||||
|
if (xdetail) {
|
||||||
|
fault_->detail = xdetail.text().as_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check Response element.
|
||||||
|
|
||||||
|
pugi::xml_node xresponse = soap_xml::GetChildNoNS(xbody,
|
||||||
|
operation_ + "Response");
|
||||||
|
|
||||||
|
if (!xresponse) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
soap_xml::SplitName(xresponse, &service_ns_.name, nullptr);
|
||||||
|
service_ns_.url = soap_xml::GetNSAttr(xresponse, service_ns_.name);
|
||||||
|
|
||||||
|
// Call result parser on each child of the response node.
|
||||||
|
if (parser_) {
|
||||||
|
pugi::xml_node xchild = xresponse.first_child();
|
||||||
|
while (xchild) {
|
||||||
|
if (!parser_(xchild)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
xchild = xchild.next_sibling();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|||||||
+45
-7
@@ -1,10 +1,13 @@
|
|||||||
#ifndef WEBCC_SOAP_RESPONSE_H_
|
#ifndef WEBCC_SOAP_RESPONSE_H_
|
||||||
#define WEBCC_SOAP_RESPONSE_H_
|
#define WEBCC_SOAP_RESPONSE_H_
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility> // for move()
|
#include <utility> // for move()
|
||||||
|
|
||||||
#include "webcc/soap_message.h"
|
#include "webcc/soap_message.h"
|
||||||
|
#include "webcc/soap_xml.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
@@ -12,6 +15,39 @@ class SoapResponse : public SoapMessage {
|
|||||||
public:
|
public:
|
||||||
SoapResponse() : is_cdata_(false) {}
|
SoapResponse() : is_cdata_(false) {}
|
||||||
|
|
||||||
|
// Response result parser.
|
||||||
|
// Called on each child of the response node.
|
||||||
|
// Example:
|
||||||
|
// - SOAP action: xxx
|
||||||
|
// - Given SOAP response:
|
||||||
|
// <soap:Body>
|
||||||
|
// <n:xxxResponse xmlns:n="...">
|
||||||
|
// <n:aaa>Blaah</aaa>
|
||||||
|
// <n:bbb>Blaah</bbb>
|
||||||
|
// <n:ccc>Blaah</ccc>
|
||||||
|
// </n:xxxResponse>
|
||||||
|
// <soap:Body>
|
||||||
|
// The parser will be called in the following sequence:
|
||||||
|
// - parser(xaaa); // return true
|
||||||
|
// - parser(xbbb); // return true
|
||||||
|
// - parser(xccc);
|
||||||
|
// If any of the parser returns false, the call sequence will be stopped:
|
||||||
|
// - parser(xaaa); // return false
|
||||||
|
// <stopped>
|
||||||
|
// Then you can get the expected result by parsing the node one by one.
|
||||||
|
// When you implement the parser, you normally need to check the node name,
|
||||||
|
// the following helper can extract the name without any namespace prefix:
|
||||||
|
// webcc::soap_xml::GetNameNoPrefix(xnode);
|
||||||
|
typedef std::function<bool(pugi::xml_node)> Parser;
|
||||||
|
|
||||||
|
void set_parser(Parser parser) {
|
||||||
|
parser_ = parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<SoapFault> fault() const {
|
||||||
|
return fault_;
|
||||||
|
}
|
||||||
|
|
||||||
// Could be "Price" for an operation/method like "GetXyzPrice".
|
// Could be "Price" for an operation/method like "GetXyzPrice".
|
||||||
// Really depend on the service.
|
// Really depend on the service.
|
||||||
// Most services use a general name "Result".
|
// Most services use a general name "Result".
|
||||||
@@ -19,35 +55,37 @@ class SoapResponse : public SoapMessage {
|
|||||||
result_name_ = result_name;
|
result_name_ = result_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Server only.
|
||||||
void set_result(const std::string& result, bool is_cdata) {
|
void set_result(const std::string& result, bool is_cdata) {
|
||||||
result_ = result;
|
result_ = result;
|
||||||
is_cdata_ = is_cdata;
|
is_cdata_ = is_cdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Server only.
|
||||||
void set_result_moved(std::string&& result, bool is_cdata) {
|
void set_result_moved(std::string&& result, bool is_cdata) {
|
||||||
result_ = std::move(result);
|
result_ = std::move(result);
|
||||||
is_cdata_ = is_cdata;
|
is_cdata_ = is_cdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string result_moved() {
|
|
||||||
return std::move(result_);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void ToXmlBody(pugi::xml_node xbody) override;
|
void ToXmlBody(pugi::xml_node xbody) override;
|
||||||
|
|
||||||
bool FromXmlBody(pugi::xml_node xbody) override;
|
bool FromXmlBody(pugi::xml_node xbody) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// NOTE:
|
// Fault element if any.
|
||||||
// Multiple results might be necessary. But for most cases, single result
|
std::shared_ptr<SoapFault> fault_;
|
||||||
// should be enough, because an API normally returns only one value.
|
|
||||||
|
// Response result parser.
|
||||||
|
Parser parser_;
|
||||||
|
|
||||||
// Result XML node name.
|
// Result XML node name.
|
||||||
// Used to parse the response XML from client side.
|
// Used to parse the response XML from client side.
|
||||||
|
// TODO
|
||||||
std::string result_name_;
|
std::string result_name_;
|
||||||
|
|
||||||
// Result value.
|
// Result value.
|
||||||
|
// TODO
|
||||||
std::string result_;
|
std::string result_;
|
||||||
|
|
||||||
// CDATA result.
|
// CDATA result.
|
||||||
|
|||||||
+15
-2
@@ -7,7 +7,7 @@ void SplitName(const pugi::xml_node& xnode, std::string* prefix,
|
|||||||
std::string* name) {
|
std::string* name) {
|
||||||
std::string full_name = xnode.name();
|
std::string full_name = xnode.name();
|
||||||
|
|
||||||
size_t pos = full_name.find(':');
|
std::size_t pos = full_name.find(':');
|
||||||
|
|
||||||
if (pos != std::string::npos) {
|
if (pos != std::string::npos) {
|
||||||
if (prefix != nullptr) {
|
if (prefix != nullptr) {
|
||||||
@@ -38,6 +38,19 @@ std::string GetNameNoPrefix(const pugi::xml_node& xnode) {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE:
|
||||||
|
// The following 3 ways all work for PCDATA and CDATA:
|
||||||
|
// - xnode.text().get()
|
||||||
|
// - xnode.text().as_string()
|
||||||
|
// - xnode.child_value()
|
||||||
|
std::string GetText(const pugi::xml_node& xnode) {
|
||||||
|
return xnode.child_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetText(const pugi::xml_node& xnode, std::string* text) {
|
||||||
|
*text = xnode.child_value();
|
||||||
|
}
|
||||||
|
|
||||||
pugi::xml_node AddChild(pugi::xml_node xnode,
|
pugi::xml_node AddChild(pugi::xml_node xnode,
|
||||||
const std::string& ns, const std::string& name) {
|
const std::string& ns, const std::string& name) {
|
||||||
return xnode.append_child((ns + ":" + name).c_str());
|
return xnode.append_child((ns + ":" + name).c_str());
|
||||||
@@ -55,7 +68,7 @@ pugi::xml_node GetChildNoNS(const pugi::xml_node& xnode,
|
|||||||
std::string child_name = xchild.name();
|
std::string child_name = xchild.name();
|
||||||
|
|
||||||
// Remove NS prefix.
|
// Remove NS prefix.
|
||||||
size_t pos = child_name.find(':');
|
std::size_t pos = child_name.find(':');
|
||||||
if (pos != std::string::npos) {
|
if (pos != std::string::npos) {
|
||||||
child_name = child_name.substr(pos + 1);
|
child_name = child_name.substr(pos + 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,15 @@ std::string GetPrefix(const pugi::xml_node& xnode);
|
|||||||
// Get the node name without namespace prefix.
|
// Get the node name without namespace prefix.
|
||||||
std::string GetNameNoPrefix(const pugi::xml_node& xnode);
|
std::string GetNameNoPrefix(const pugi::xml_node& xnode);
|
||||||
|
|
||||||
|
// Get node text (applicable for both PCDATA and CDATA).
|
||||||
|
// E.g., given the following node:
|
||||||
|
// <Name>Chunting Gu</Name>
|
||||||
|
// GetText returns "Chunting Gu".
|
||||||
|
std::string GetText(const pugi::xml_node& xnode);
|
||||||
|
|
||||||
|
// Output parameter version GetText.
|
||||||
|
void GetText(const pugi::xml_node& xnode, std::string* text);
|
||||||
|
|
||||||
// Add a child with the given name which is prefixed by a namespace.
|
// Add a child with the given name which is prefixed by a namespace.
|
||||||
// E.g., AppendChild(xnode, "soapenv", "Envelope") will append a child with
|
// E.g., AppendChild(xnode, "soapenv", "Envelope") will append a child with
|
||||||
// name "soapenv:Envelope".
|
// name "soapenv:Envelope".
|
||||||
|
|||||||
Reference in New Issue
Block a user