Improve the response result composition.

This commit is contained in:
Chunting Gu
2018-09-27 17:05:29 +08:00
parent 76417c3703
commit b02ea902c9
8 changed files with 128 additions and 67 deletions
+1 -1
View File
@@ -92,7 +92,7 @@ void SoapAsyncClient::ResponseHandler(SoapResponseHandler soap_response_handler,
soap_response_handler("", error, timed_out);
} else {
SoapResponse soap_response;
soap_response.set_result_name(result_name_);
//soap_response.set_result_name(result_name_);
if (!soap_response.FromXml(http_response->content())) {
soap_response_handler("", kXmlError, false);
+18 -19
View File
@@ -7,22 +7,23 @@
namespace webcc {
void SoapResponse::ToXmlBody(pugi::xml_node xbody) {
assert(!result_name_.empty());
pugi::xml_node xresponse = soap_xml::AddChild(xbody, service_ns_.name,
operation_ + "Response");
soap_xml::AddNSAttr(xresponse, service_ns_.name, service_ns_.url);
pugi::xml_node xop = soap_xml::AddChild(xbody, service_ns_.name,
operation_ + "Response");
soap_xml::AddNSAttr(xop, service_ns_.name, service_ns_.url);
pugi::xml_node xresult = soap_xml::AddChild(xop, service_ns_.name,
result_name_);
// 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)
.set_value(result_.c_str());
if (simple_result_) {
pugi::xml_node xresult = soap_xml::AddChild(xresponse, service_ns_.name,
simple_result_->name);
soap_xml::SetText(xresult, simple_result_->value, simple_result_->is_cdata);
} else {
assert(composer_);
(*composer_)(xresponse);
}
}
bool SoapResponse::FromXmlBody(pugi::xml_node xbody) {
assert(parser_);
// Check Fault element.
pugi::xml_node xfault = soap_xml::GetChildNoNS(xbody, "Fault");
@@ -62,14 +63,12 @@ bool SoapResponse::FromXmlBody(pugi::xml_node xbody) {
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();
pugi::xml_node xchild = xresponse.first_child();
while (xchild) {
if (!parser_(xchild)) {
break;
}
xchild = xchild.next_sibling();
}
return true;
+91 -36
View File
@@ -13,8 +13,6 @@ namespace webcc {
class SoapResponse : public SoapMessage {
public:
SoapResponse() : is_cdata_(false) {}
// Response result parser.
// Called on each child of the response node.
// Example:
@@ -28,44 +26,106 @@ class SoapResponse : public SoapMessage {
// </n:xxxResponse>
// <soap:Body>
// The parser will be called in the following sequence:
// - parser(xaaa); // return true
// - parser(xbbb); // return true
// - parser(xccc);
// - parser(aaa); // return true
// - parser(bbb); // return true
// - parser(ccc);
// If any of the parser returns false, the call sequence will be stopped:
// - parser(xaaa); // return false
// - parser(aaa); // 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);
// webcc::soap_xml::GetNameNoPrefix(node);
typedef std::function<bool(pugi::xml_node)> Parser;
// Response result composer.
// Called on the response node.
// Example:
// - SOAP action: xxx
// - Given SOAP response:
// <soap:Body>
// <n:xxxResponse xmlns:n="..." />
// <soap:Body>
// The composer will be called as:
// - composer(xxxResponse);
// The composer then add proper children to xxxResponse as the result.
class Composer {
public:
void operator()(pugi::xml_node xresponse) {
Compose(xresponse);
}
private:
virtual void Compose(pugi::xml_node xresponse) = 0;
};
typedef std::shared_ptr<Composer> ComposerPtr;
// Simple result means there's only one child of the response node.
// Normally, the value of the result is a string (could be CDATA to embed an
// XML string).
// Given SOAP action "xxx", the response could be:
// - Plain text as result value:
// <soap:Body>
// <n:xxxResponse xmlns:n="...">
// <n:Result>2.0</n:Result>
// </n:xxxResponse>
// <soap:Body>
// - CDATA as result value:
// <soap:Body>
// <n:xxxResponse xmlns:n="...">
// <n:Result>
// <![CDATA[
// <webcc type = "response">
// <status code = "0" message = "ok">
// <book>
// <id>1</id>
// </book>
// </webcc>
// ]]>
// </n:Result>
// </n:xxxResponse>
// <soap:Body>
struct SimpleResult {
std::string name; // Result XML node name.
std::string value; // Result value.
bool is_cdata; // CDATA result.
};
typedef std::shared_ptr<SimpleResult> SimpleResultPtr;
SoapResponse() : parser_(nullptr) {}
// Set the parser to parse the result.
// Client only.
void set_parser(Parser parser) {
parser_ = parser;
}
// Set the composer to compose the result.
// Composer will be ignored if the simple result is provided.
void set_composer(ComposerPtr composer) {
composer_ = composer;
}
// Set to compose from simple result.
void set_simple_result(SimpleResultPtr simple_result) {
simple_result_ = simple_result;
}
// Set to compose from simple result (a shortcut).
void set_simple_result(const std::string& name,
std::string&& value,
bool is_cdata) {
simple_result_.reset(new SimpleResult{
name, std::move(value), is_cdata
});
}
std::shared_ptr<SoapFault> fault() const {
return fault_;
}
// Could be "Price" for an operation/method like "GetXyzPrice".
// Really depend on the service.
// Most services use a general name "Result".
void set_result_name(const std::string& result_name) {
result_name_ = result_name;
}
// Server only.
void set_result(const std::string& result, bool is_cdata) {
result_ = result;
is_cdata_ = is_cdata;
}
// Server only.
void set_result_moved(std::string&& result, bool is_cdata) {
result_ = std::move(result);
is_cdata_ = is_cdata;
}
// TODO: Set fault from server.
protected:
void ToXmlBody(pugi::xml_node xbody) override;
@@ -76,20 +136,15 @@ class SoapResponse : public SoapMessage {
// Fault element if any.
std::shared_ptr<SoapFault> fault_;
// Response result parser.
// Result parser (for client).
Parser parser_;
// Result XML node name.
// Used to parse the response XML from client side.
// TODO
std::string result_name_;
// Result composer (for server).
// Ignored if |simple_result_| is provided.
ComposerPtr composer_;
// Result value.
// TODO
std::string result_;
// CDATA result.
bool is_cdata_;
// Simple result (for server).
SimpleResultPtr simple_result_;
};
} // namespace webcc
+5
View File
@@ -51,6 +51,11 @@ void GetText(const pugi::xml_node& xnode, std::string* text) {
*text = xnode.child_value();
}
void SetText(pugi::xml_node xnode, const std::string& text, bool is_cdata) {
xnode.append_child(is_cdata ? pugi::node_cdata : pugi::node_pcdata)
.set_value(text.c_str());
}
pugi::xml_node AddChild(pugi::xml_node xnode,
const std::string& ns, const std::string& name) {
return xnode.append_child((ns + ":" + name).c_str());
+3
View File
@@ -32,6 +32,9 @@ std::string GetText(const pugi::xml_node& xnode);
// Output parameter version GetText.
void GetText(const pugi::xml_node& xnode, std::string* text);
// Set node text.
void SetText(pugi::xml_node xnode, const std::string& text, bool is_cdata);
// Add a child with the given name which is prefixed by a namespace.
// E.g., AppendChild(xnode, "soapenv", "Envelope") will append a child with
// name "soapenv:Envelope".