Improve the response result composition.
This commit is contained in:
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
#include "example/common/book_xml.h"
|
#include "example/common/book_xml.h"
|
||||||
|
|
||||||
static const std::string kResultName = "Result";
|
static const std::string kResult = "Result";
|
||||||
|
|
||||||
static void PrintSeparateLine() {
|
static void PrintSeparateLine() {
|
||||||
std::cout << "--------------------------------";
|
std::cout << "--------------------------------";
|
||||||
@@ -104,7 +104,7 @@ 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), kResultName,
|
if (!soap_client_.Request(operation, std::move(parameters), kResult,
|
||||||
result_str)) {
|
result_str)) {
|
||||||
PrintError();
|
PrintError();
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
|
|
||||||
static BookStore g_book_store;
|
static BookStore g_book_store;
|
||||||
|
|
||||||
|
static const std::string kResult = "Result";
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
bool BookService::Handle(const webcc::SoapRequest& soap_request,
|
bool BookService::Handle(const webcc::SoapRequest& soap_request,
|
||||||
@@ -27,7 +29,6 @@ bool BookService::Handle(const webcc::SoapRequest& soap_request,
|
|||||||
});
|
});
|
||||||
|
|
||||||
soap_response->set_operation(operation);
|
soap_response->set_operation(operation);
|
||||||
soap_response->set_result_name("Result");
|
|
||||||
|
|
||||||
if (operation == "CreateBook") {
|
if (operation == "CreateBook") {
|
||||||
return CreateBook(soap_request, soap_response);
|
return CreateBook(soap_request, soap_response);
|
||||||
@@ -97,7 +98,7 @@ bool BookService::CreateBook(const webcc::SoapRequest& soap_request,
|
|||||||
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_simple_result(kResult, std::move(response_xml), true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -137,7 +138,7 @@ bool BookService::GetBook(const webcc::SoapRequest& soap_request,
|
|||||||
|
|
||||||
const Book& book = g_book_store.GetBook(id);
|
const Book& book = g_book_store.GetBook(id);
|
||||||
|
|
||||||
soap_response->set_result_moved(NewResultXml(0, "ok", book), true);
|
soap_response->set_simple_result(kResult, NewResultXml(0, "ok", book), true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -176,7 +177,7 @@ bool BookService::ListBooks(const webcc::SoapRequest& soap_request,
|
|||||||
|
|
||||||
const std::list<Book>& books = g_book_store.books();
|
const std::list<Book>& books = g_book_store.books();
|
||||||
|
|
||||||
soap_response->set_result_moved(NewResultXml(0, "ok", books), true);
|
soap_response->set_simple_result(kResult, NewResultXml(0, "ok", books), true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -210,9 +211,9 @@ bool BookService::DeleteBook(const webcc::SoapRequest& soap_request,
|
|||||||
const std::string& id = soap_request.GetParameter("id");
|
const std::string& id = soap_request.GetParameter("id");
|
||||||
|
|
||||||
if (g_book_store.DeleteBook(id)) {
|
if (g_book_store.DeleteBook(id)) {
|
||||||
soap_response->set_result_moved(NewResultXml(0, "ok"), true);
|
soap_response->set_simple_result(kResult, NewResultXml(0, "ok"), true);
|
||||||
} else {
|
} else {
|
||||||
soap_response->set_result_moved(NewResultXml(1, "error"), true);
|
soap_response->set_simple_result(kResult, NewResultXml(1, "error"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -58,9 +58,7 @@ bool CalcService::Handle(const webcc::SoapRequest& soap_request,
|
|||||||
});
|
});
|
||||||
|
|
||||||
soap_response->set_operation(soap_request.operation());
|
soap_response->set_operation(soap_request.operation());
|
||||||
|
soap_response->set_simple_result("Result", std::to_string(result), false);
|
||||||
soap_response->set_result_name("Result");
|
|
||||||
soap_response->set_result_moved(std::to_string(result), false);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ void SoapAsyncClient::ResponseHandler(SoapResponseHandler soap_response_handler,
|
|||||||
soap_response_handler("", error, timed_out);
|
soap_response_handler("", error, timed_out);
|
||||||
} else {
|
} else {
|
||||||
SoapResponse soap_response;
|
SoapResponse soap_response;
|
||||||
soap_response.set_result_name(result_name_);
|
//soap_response.set_result_name(result_name_);
|
||||||
|
|
||||||
if (!soap_response.FromXml(http_response->content())) {
|
if (!soap_response.FromXml(http_response->content())) {
|
||||||
soap_response_handler("", kXmlError, false);
|
soap_response_handler("", kXmlError, false);
|
||||||
|
|||||||
+18
-19
@@ -7,22 +7,23 @@
|
|||||||
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 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,
|
if (simple_result_) {
|
||||||
operation_ + "Response");
|
pugi::xml_node xresult = soap_xml::AddChild(xresponse, service_ns_.name,
|
||||||
soap_xml::AddNSAttr(xop, service_ns_.name, service_ns_.url);
|
simple_result_->name);
|
||||||
|
soap_xml::SetText(xresult, simple_result_->value, simple_result_->is_cdata);
|
||||||
pugi::xml_node xresult = soap_xml::AddChild(xop, service_ns_.name,
|
} else {
|
||||||
result_name_);
|
assert(composer_);
|
||||||
|
(*composer_)(xresponse);
|
||||||
// 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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SoapResponse::FromXmlBody(pugi::xml_node xbody) {
|
bool SoapResponse::FromXmlBody(pugi::xml_node xbody) {
|
||||||
|
assert(parser_);
|
||||||
|
|
||||||
// Check Fault element.
|
// Check Fault element.
|
||||||
|
|
||||||
pugi::xml_node xfault = soap_xml::GetChildNoNS(xbody, "Fault");
|
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);
|
service_ns_.url = soap_xml::GetNSAttr(xresponse, service_ns_.name);
|
||||||
|
|
||||||
// Call result parser on each child of the response node.
|
// Call result parser on each child of the response node.
|
||||||
if (parser_) {
|
pugi::xml_node xchild = xresponse.first_child();
|
||||||
pugi::xml_node xchild = xresponse.first_child();
|
while (xchild) {
|
||||||
while (xchild) {
|
if (!parser_(xchild)) {
|
||||||
if (!parser_(xchild)) {
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
xchild = xchild.next_sibling();
|
|
||||||
}
|
}
|
||||||
|
xchild = xchild.next_sibling();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
+91
-36
@@ -13,8 +13,6 @@ namespace webcc {
|
|||||||
|
|
||||||
class SoapResponse : public SoapMessage {
|
class SoapResponse : public SoapMessage {
|
||||||
public:
|
public:
|
||||||
SoapResponse() : is_cdata_(false) {}
|
|
||||||
|
|
||||||
// Response result parser.
|
// Response result parser.
|
||||||
// Called on each child of the response node.
|
// Called on each child of the response node.
|
||||||
// Example:
|
// Example:
|
||||||
@@ -28,44 +26,106 @@ class SoapResponse : public SoapMessage {
|
|||||||
// </n:xxxResponse>
|
// </n:xxxResponse>
|
||||||
// <soap:Body>
|
// <soap:Body>
|
||||||
// The parser will be called in the following sequence:
|
// The parser will be called in the following sequence:
|
||||||
// - parser(xaaa); // return true
|
// - parser(aaa); // return true
|
||||||
// - parser(xbbb); // return true
|
// - parser(bbb); // return true
|
||||||
// - parser(xccc);
|
// - parser(ccc);
|
||||||
// If any of the parser returns false, the call sequence will be stopped:
|
// If any of the parser returns false, the call sequence will be stopped:
|
||||||
// - parser(xaaa); // return false
|
// - parser(aaa); // return false
|
||||||
// <stopped>
|
// <stopped>
|
||||||
// Then you can get the expected result by parsing the node one by one.
|
// 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,
|
// When you implement the parser, you normally need to check the node name,
|
||||||
// the following helper can extract the name without any namespace prefix:
|
// 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;
|
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) {
|
void set_parser(Parser 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 {
|
std::shared_ptr<SoapFault> fault() const {
|
||||||
return fault_;
|
return fault_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Could be "Price" for an operation/method like "GetXyzPrice".
|
// TODO: Set fault from server.
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void ToXmlBody(pugi::xml_node xbody) override;
|
void ToXmlBody(pugi::xml_node xbody) override;
|
||||||
@@ -76,20 +136,15 @@ class SoapResponse : public SoapMessage {
|
|||||||
// Fault element if any.
|
// Fault element if any.
|
||||||
std::shared_ptr<SoapFault> fault_;
|
std::shared_ptr<SoapFault> fault_;
|
||||||
|
|
||||||
// Response result parser.
|
// Result parser (for client).
|
||||||
Parser parser_;
|
Parser parser_;
|
||||||
|
|
||||||
// Result XML node name.
|
// Result composer (for server).
|
||||||
// Used to parse the response XML from client side.
|
// Ignored if |simple_result_| is provided.
|
||||||
// TODO
|
ComposerPtr composer_;
|
||||||
std::string result_name_;
|
|
||||||
|
|
||||||
// Result value.
|
// Simple result (for server).
|
||||||
// TODO
|
SimpleResultPtr simple_result_;
|
||||||
std::string result_;
|
|
||||||
|
|
||||||
// CDATA result.
|
|
||||||
bool is_cdata_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|||||||
@@ -51,6 +51,11 @@ void GetText(const pugi::xml_node& xnode, std::string* text) {
|
|||||||
*text = xnode.child_value();
|
*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,
|
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());
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ std::string GetText(const pugi::xml_node& xnode);
|
|||||||
// Output parameter version GetText.
|
// Output parameter version GetText.
|
||||||
void GetText(const pugi::xml_node& xnode, std::string* text);
|
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.
|
// 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