From 37a4fd7fe1ea5d61f25a41868c682ac02bf682c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=80=82=E7=84=B6?= Date: Sun, 24 Feb 2019 15:22:24 +0800 Subject: [PATCH 1/2] add server side query parameters support --- .../codegen/codegen_define_ApiController_.hpp | 69 ++++++++++++++++++- .../codegen/codegen_undef_ApiController_.hpp | 23 +++++++ .../web/protocol/http/incoming/Request.hpp | 40 ++++++++++- src/oatpp/web/server/api/ApiController.hpp | 1 + test/oatpp/web/FullTest.cpp | 7 ++ test/oatpp/web/app/Client.hpp | 1 + test/oatpp/web/app/Controller.hpp | 17 +++++ 7 files changed, 155 insertions(+), 3 deletions(-) diff --git a/src/oatpp/codegen/codegen_define_ApiController_.hpp b/src/oatpp/codegen/codegen_define_ApiController_.hpp index b9b75493..aab36fcb 100644 --- a/src/oatpp/codegen/codegen_define_ApiController_.hpp +++ b/src/oatpp/codegen/codegen_define_ApiController_.hpp @@ -21,7 +21,6 @@ * limitations under the License. * ***************************************************************************/ - #include "oatpp/core/macro/basic.hpp" #include "oatpp/core/macro/codegen.hpp" @@ -42,6 +41,12 @@ OATPP_MACRO_API_CONTROLLER_PARAM(OATPP_MACRO_API_CONTROLLER_HEADER, OATPP_MACRO_ #define PATH(TYPE, NAME, ...) \ OATPP_MACRO_API_CONTROLLER_PARAM(OATPP_MACRO_API_CONTROLLER_PATH, OATPP_MACRO_API_CONTROLLER_PATH_INFO, TYPE, NAME, (__VA_ARGS__)) +#define QUERIES(TYPE, NAME) \ +OATPP_MACRO_API_CONTROLLER_PARAM(OATPP_MACRO_API_CONTROLLER_QUERIES, OATPP_MACRO_API_CONTROLLER_QUERIES_INFO, TYPE, NAME, ()) + +#define QUERY(TYPE, NAME, ...) \ +OATPP_MACRO_API_CONTROLLER_PARAM(OATPP_MACRO_API_CONTROLLER_QUERY, OATPP_MACRO_API_CONTROLLER_QUERY_INFO, TYPE, NAME, (__VA_ARGS__)) + #define BODY_STRING(TYPE, NAME) \ OATPP_MACRO_API_CONTROLLER_PARAM(OATPP_MACRO_API_CONTROLLER_BODY_STRING, OATPP_MACRO_API_CONTROLLER_BODY_STRING_INFO, TYPE, NAME, ()) @@ -56,6 +61,7 @@ TYPE NAME = __request; #define OATPP_MACRO_API_CONTROLLER_REQUEST_INFO(TYPE, NAME, PARAM_LIST) + // HEADER MACRO // ------------------------------------------------------ #define OATPP_MACRO_API_CONTROLLER_HEADER_0(TYPE, NAME, PARAM_LIST) \ @@ -164,6 +170,67 @@ OATPP_MACRO_API_CONTROLLER_PATH_INFO_CHOOSER (TYPE, NAME, PARAM_LIST, HAS_ARGS) #define OATPP_MACRO_API_CONTROLLER_PATH_INFO(TYPE, NAME, PARAM_LIST) \ OATPP_MACRO_API_CONTROLLER_PATH_INFO_CHOOSER_EXP(TYPE, NAME, PARAM_LIST, OATPP_MACRO_HAS_ARGS PARAM_LIST); +// QUERIES MACRO // ------------------------------------------------------ + +#define OATPP_MACRO_API_CONTROLLER_QUERIES(TYPE, NAME, PARAM_LIST) \ +TYPE NAME = __request->getQueryParameters(); + +#define OATPP_MACRO_API_CONTROLLER_QUERIES_INFO(TYPE, NAME, PARAM_LIST) + +// QUERY MACRO // ------------------------------------------------------ + +#define OATPP_MACRO_API_CONTROLLER_QUERY_0(TYPE, NAME, PARAM_LIST) \ +auto __param_str_val_##NAME = __request->getQueryParameter(#NAME); \ +if(!__param_str_val_##NAME){ \ + return ApiController::handleError(Status::CODE_400, "Missing QUERY parameter '" #NAME "'"); \ +} \ +bool __param_validation_check_##NAME; \ +TYPE NAME = TYPE::Class::parseFromString(__param_str_val_##NAME, __param_validation_check_##NAME); \ +if(!__param_validation_check_##NAME){ \ + return ApiController::handleError(Status::CODE_400, "Invalid QUERY parameter '" #NAME "'. Expected type is '" #TYPE "'"); \ +} + +#define OATPP_MACRO_API_CONTROLLER_QUERY_1(TYPE, NAME, PARAM_LIST) \ +auto __param_str_val_##NAME = __request->getQueryParameter(OATPP_MACRO_FIRSTARG PARAM_LIST); \ +if(!__param_str_val_##NAME){ \ + return ApiController::handleError(Status::CODE_400, \ + oatpp::String("Missing QUERY parameter '") + OATPP_MACRO_FIRSTARG PARAM_LIST + "'"); \ +} \ +bool __param_validation_check_##NAME; \ +TYPE NAME = TYPE::Class::parseFromString(__param_str_val_##NAME, __param_validation_check_##NAME); \ +if(!__param_validation_check_##NAME){ \ + return ApiController::handleError(Status::CODE_400, \ + oatpp::String("Invalid QUERY parameter '") + \ + OATPP_MACRO_FIRSTARG PARAM_LIST + \ + "'. Expected type is '" #TYPE "'"); \ +} + +#define OATPP_MACRO_API_CONTROLLER_QUERY_CHOOSER(TYPE, NAME, PARAM_LIST, HAS_ARGS) \ +OATPP_MACRO_API_CONTROLLER_QUERY_##HAS_ARGS (TYPE, NAME, PARAM_LIST) + +#define OATPP_MACRO_API_CONTROLLER_QUERY_CHOOSER_EXP(TYPE, NAME, PARAM_LIST, HAS_ARGS) \ +OATPP_MACRO_API_CONTROLLER_QUERY_CHOOSER (TYPE, NAME, PARAM_LIST, HAS_ARGS) + +#define OATPP_MACRO_API_CONTROLLER_QUERY(TYPE, NAME, PARAM_LIST) \ +OATPP_MACRO_API_CONTROLLER_QUERY_CHOOSER_EXP(TYPE, NAME, PARAM_LIST, OATPP_MACRO_HAS_ARGS PARAM_LIST); + +// __INFO + +#define OATPP_MACRO_API_CONTROLLER_QUERY_INFO_0(TYPE, NAME, PARAM_LIST) \ +info->queryParams.push_back(Endpoint::Info::Param(#NAME, TYPE::Class::getType())); + +#define OATPP_MACRO_API_CONTROLLER_QUERY_INFO_1(TYPE, NAME, PARAM_LIST) \ +info->queryParams.push_back(Endpoint::Info::Param(OATPP_MACRO_FIRSTARG PARAM_LIST, TYPE::Class::getType())); + +#define OATPP_MACRO_API_CONTROLLER_QUERY_INFO_CHOOSER(TYPE, NAME, PARAM_LIST, HAS_ARGS) \ +OATPP_MACRO_API_CONTROLLER_QUERY_INFO_##HAS_ARGS (TYPE, NAME, PARAM_LIST) + +#define OATPP_MACRO_API_CONTROLLER_QUERY_INFO_CHOOSER_EXP(TYPE, NAME, PARAM_LIST, HAS_ARGS) \ +OATPP_MACRO_API_CONTROLLER_QUERY_INFO_CHOOSER (TYPE, NAME, PARAM_LIST, HAS_ARGS) + +#define OATPP_MACRO_API_CONTROLLER_QUERY_INFO(TYPE, NAME, PARAM_LIST) \ +OATPP_MACRO_API_CONTROLLER_QUERY_INFO_CHOOSER_EXP(TYPE, NAME, PARAM_LIST, OATPP_MACRO_HAS_ARGS PARAM_LIST); + // BODY_STRING MACRO // ------------------------------------------------------ #define OATPP_MACRO_API_CONTROLLER_BODY_STRING(TYPE, NAME, PARAM_LIST) \ diff --git a/src/oatpp/codegen/codegen_undef_ApiController_.hpp b/src/oatpp/codegen/codegen_undef_ApiController_.hpp index 29557299..2f37ed7b 100644 --- a/src/oatpp/codegen/codegen_undef_ApiController_.hpp +++ b/src/oatpp/codegen/codegen_undef_ApiController_.hpp @@ -33,6 +33,8 @@ #undef REQUEST #undef HEADER #undef PATH +#undef QUERIES +#undef QUERY #undef BODY_STRING #undef BODY_DTO @@ -77,6 +79,27 @@ #undef OATPP_MACRO_API_CONTROLLER_PATH_INFO_CHOOSER_EXP #undef OATPP_MACRO_API_CONTROLLER_PATH_INFO +// QUERIES MACRO // ------------------------------------------------------ + +#undef OATPP_MACRO_API_CONTROLLER_QUERIES +#undef OATPP_MACRO_API_CONTROLLER_QUERIES_INFO + +// QUERY MACRO // ------------------------------------------------------ + +#undef OATPP_MACRO_API_CONTROLLER_QUERY_0 +#undef OATPP_MACRO_API_CONTROLLER_QUERY_1 +#undef OATPP_MACRO_API_CONTROLLER_QUERY_CHOOSER +#undef OATPP_MACRO_API_CONTROLLER_QUERY_CHOOSER_EXP +#undef OATPP_MACRO_API_CONTROLLER_QUERY + +// __INFO + +#undef OATPP_MACRO_API_CONTROLLER_QUERY_INFO_0 +#undef OATPP_MACRO_API_CONTROLLER_QUERY_INFO_1 +#undef OATPP_MACRO_API_CONTROLLER_QUERY_INFO_CHOOSER +#undef OATPP_MACRO_API_CONTROLLER_QUERY_INFO_CHOOSER_EXP +#undef OATPP_MACRO_API_CONTROLLER_QUERY_INFO + // BODY_STRING MACRO // ------------------------------------------------------ #undef OATPP_MACRO_API_CONTROLLER_BODY_STRING diff --git a/src/oatpp/web/protocol/http/incoming/Request.hpp b/src/oatpp/web/protocol/http/incoming/Request.hpp index 29f5185a..eeeca799 100644 --- a/src/oatpp/web/protocol/http/incoming/Request.hpp +++ b/src/oatpp/web/protocol/http/incoming/Request.hpp @@ -28,6 +28,7 @@ #include "oatpp/web/protocol/http/Http.hpp" #include "oatpp/web/protocol/http/incoming/BodyDecoder.hpp" #include "oatpp/web/url/mapping/Pattern.hpp" +#include "oatpp/network/Url.hpp" namespace oatpp { namespace web { namespace protocol { namespace http { namespace incoming { @@ -36,16 +37,22 @@ public: OBJECT_POOL(Incoming_Request_Pool, Request, 32) SHARED_OBJECT_POOL(Shared_Incoming_Request_Pool, Request, 32) private: + struct StdStringComp { + public: + bool operator()(const std::string& a, const std::string& b) const { return a.compare(b) > 0; } + }; http::RequestStartingLine m_startingLine; url::mapping::Pattern::MatchMap m_pathVariables; http::Protocol::Headers m_headers; std::shared_ptr m_bodyStream; + std::shared_ptr> m_queryParams; /** * Request should be preconfigured with default BodyDecoder. * Custom BodyDecoder can be set on demand */ std::shared_ptr m_bodyDecoder; + public: /* Request(const std::shared_ptr& pBodyDecoder) @@ -57,13 +64,24 @@ public: const url::mapping::Pattern::MatchMap& pathVariables, const http::Protocol::Headers& headers, const std::shared_ptr& bodyStream, - const std::shared_ptr& bodyDecoder) + const std::shared_ptr& bodyDecoder + ) : m_startingLine(startingLine) , m_pathVariables(pathVariables) , m_headers(headers) , m_bodyStream(bodyStream) , m_bodyDecoder(bodyDecoder) - {} + { + m_queryParams = std::make_shared>(); + auto params = oatpp::network::Url::Parser::parseQueryParams(pathVariables.getTail()); + auto param = params->getFirstEntry(); + while(param != nullptr) { + std::string key = param->getKey().get()->std_str(); + oatpp::String value = param->getValue(); + (*m_queryParams)[key] = value; + param = param->getNext(); + } + } public: static std::shared_ptr createShared(const http::RequestStartingLine& startingLine, @@ -110,6 +128,24 @@ public: return m_pathVariables.getTail(); } + oatpp::String getQueryParameter(const std::string& key) const { + auto iter = m_queryParams->find(key); + if (iter == m_queryParams->end()) { + return nullptr; + } else { + return iter->second; + } + } + + oatpp::String getQueryParameter(std::string key, oatpp::String& _value) { + auto value = getQueryParameter(key); + return value == nullptr ? _value : value; + } + + std::shared_ptr> getQueryParameters() { + return m_queryParams; + } + void streamBody(const std::shared_ptr& toStream) const { m_bodyDecoder->decode(m_headers, m_bodyStream, toStream); } diff --git a/src/oatpp/web/server/api/ApiController.hpp b/src/oatpp/web/server/api/ApiController.hpp index a6ef30f3..3e2a1d84 100644 --- a/src/oatpp/web/server/api/ApiController.hpp +++ b/src/oatpp/web/server/api/ApiController.hpp @@ -54,6 +54,7 @@ public: typedef oatpp::web::protocol::http::Header Header; typedef oatpp::web::server::api::Endpoint Endpoint; typedef oatpp::collection::LinkedList> Endpoints; + typedef std::shared_ptr> QueryParameters; public: typedef oatpp::data::mapping::ObjectMapper ObjectMapper; diff --git a/test/oatpp/web/FullTest.cpp b/test/oatpp/web/FullTest.cpp index fc8c8e59..cb6d175d 100644 --- a/test/oatpp/web/FullTest.cpp +++ b/test/oatpp/web/FullTest.cpp @@ -88,6 +88,13 @@ bool FullTest::onRun() { OATPP_ASSERT(dto->testValue == "my_test_param"); } + { // test GET with query parameters + auto response = client->getWithQueries("oatpp", 1); + auto dto = response->readBodyToDto(objectMapper); + OATPP_ASSERT(dto); + OATPP_ASSERT(dto->testValue == "age=1&name=oatpp"); + } + { // test GET with header parameter auto response = client->getWithHeaders("my_test_header"); auto dto = response->readBodyToDto(objectMapper); diff --git a/test/oatpp/web/app/Client.hpp b/test/oatpp/web/app/Client.hpp index 862fd8ef..ba414429 100644 --- a/test/oatpp/web/app/Client.hpp +++ b/test/oatpp/web/app/Client.hpp @@ -37,6 +37,7 @@ class Client : public oatpp::web::client::ApiClient { API_CALL("GET", "/", getRoot) API_CALL("GET", "params/{param}", getWithParams, PATH(String, param)) + API_CALL("GET", "queries", getWithQueries, QUERY(String, name), QUERY(Int32, age)) API_CALL("GET", "headers", getWithHeaders, HEADER(String, param, "X-TEST-HEADER")) API_CALL("POST", "body", postBody, BODY_STRING(String, body)) API_CALL("POST", "echo", echoBody, BODY_STRING(String, body)) diff --git a/test/oatpp/web/app/Controller.hpp b/test/oatpp/web/app/Controller.hpp index 31ac7122..1083b63d 100644 --- a/test/oatpp/web/app/Controller.hpp +++ b/test/oatpp/web/app/Controller.hpp @@ -25,6 +25,7 @@ #ifndef oatpp_test_web_app_Controller_hpp #define oatpp_test_web_app_Controller_hpp +#include #include "./DTOs.hpp" #include "oatpp/web/server/api/ApiController.hpp" #include "oatpp/parser/json/mapping/ObjectMapper.hpp" @@ -61,6 +62,22 @@ public: return createDtoResponse(Status::CODE_200, dto); } + ENDPOINT("GET", "queries/*", getWithQueries, + QUERIES(QueryParameters, queries), QUERY(String, name), QUERY(Int32, age)) { + std::ostringstream builder; + for (auto i = queries->begin(); i != queries->end(); ++i) { + if (i != queries->begin()) { + builder << "&"; + } + builder << i->first << "=" << i->second->std_str(); + } + auto queryString = builder.str(); + OATPP_LOGD(TAG, "GET queries?%s =>(name=%s, age=%d)", queryString.c_str(), name->c_str(), age->getValue()); + auto dto = TestDto::createShared(); + dto->testValue = queryString.c_str(); + return createDtoResponse(Status::CODE_200, dto); + } + ENDPOINT("GET", "headers", getWithHeaders, HEADER(String, param, "X-TEST-HEADER")) { OATPP_LOGD(TAG, "GET headers {X-TEST-HEADER: %s}", param->c_str()); From c6c41034c033867458b965ba0c2b5a88ab5d8dbc Mon Sep 17 00:00:00 2001 From: lganzzzo Date: Sun, 24 Feb 2019 13:31:57 +0200 Subject: [PATCH 2/2] pr-review --- src/oatpp/network/Url.hpp | 4 +- src/oatpp/web/protocol/http/Http.hpp | 1 + .../web/protocol/http/incoming/Request.hpp | 65 ++++++++++--------- src/oatpp/web/server/api/ApiController.hpp | 2 +- test/oatpp/web/FullTest.cpp | 2 +- test/oatpp/web/app/Controller.hpp | 18 +++-- 6 files changed, 52 insertions(+), 40 deletions(-) diff --git a/src/oatpp/network/Url.hpp b/src/oatpp/network/Url.hpp index 84b7dafa..720dc69d 100644 --- a/src/oatpp/network/Url.hpp +++ b/src/oatpp/network/Url.hpp @@ -30,7 +30,9 @@ #include "oatpp/core/Types.hpp" namespace oatpp { namespace network { - + + +// TODO - refactor to use oatpp::data::share::MemoryLabel class Url : public oatpp::base::Controllable { public: typedef oatpp::collection::ListMap Parameters; diff --git a/src/oatpp/web/protocol/http/Http.hpp b/src/oatpp/web/protocol/http/Http.hpp index 83f07bfc..d35ffc81 100644 --- a/src/oatpp/web/protocol/http/Http.hpp +++ b/src/oatpp/web/protocol/http/Http.hpp @@ -263,6 +263,7 @@ struct ResponseStartingLine { class Protocol { public: typedef std::unordered_map Headers; + typedef std::unordered_map QueryParams; private: static oatpp::data::share::StringKeyLabelCI_FAST parseHeaderNameLabel(const std::shared_ptr& headersText, oatpp::parser::Caret& caret); diff --git a/src/oatpp/web/protocol/http/incoming/Request.hpp b/src/oatpp/web/protocol/http/incoming/Request.hpp index eeeca799..9a2b2c92 100644 --- a/src/oatpp/web/protocol/http/incoming/Request.hpp +++ b/src/oatpp/web/protocol/http/incoming/Request.hpp @@ -37,22 +37,21 @@ public: OBJECT_POOL(Incoming_Request_Pool, Request, 32) SHARED_OBJECT_POOL(Shared_Incoming_Request_Pool, Request, 32) private: - struct StdStringComp { - public: - bool operator()(const std::string& a, const std::string& b) const { return a.compare(b) > 0; } - }; + http::RequestStartingLine m_startingLine; url::mapping::Pattern::MatchMap m_pathVariables; http::Protocol::Headers m_headers; std::shared_ptr m_bodyStream; - std::shared_ptr> m_queryParams; + + mutable bool m_queryParamsParsed; // used for lazy parsing of QueryParams + mutable http::Protocol::QueryParams m_queryParams; /** * Request should be preconfigured with default BodyDecoder. * Custom BodyDecoder can be set on demand */ std::shared_ptr m_bodyDecoder; - + public: /* Request(const std::shared_ptr& pBodyDecoder) @@ -65,23 +64,14 @@ public: const http::Protocol::Headers& headers, const std::shared_ptr& bodyStream, const std::shared_ptr& bodyDecoder - ) + ) : m_startingLine(startingLine) , m_pathVariables(pathVariables) , m_headers(headers) , m_bodyStream(bodyStream) , m_bodyDecoder(bodyDecoder) - { - m_queryParams = std::make_shared>(); - auto params = oatpp::network::Url::Parser::parseQueryParams(pathVariables.getTail()); - auto param = params->getFirstEntry(); - while(param != nullptr) { - std::string key = param->getKey().get()->std_str(); - oatpp::String value = param->getValue(); - (*m_queryParams)[key] = value; - param = param->getNext(); - } - } + , m_queryParamsParsed(false) + {} public: static std::shared_ptr createShared(const http::RequestStartingLine& startingLine, @@ -99,7 +89,26 @@ public: const url::mapping::Pattern::MatchMap& getPathVariables() const { return m_pathVariables; } - + + /** + * Get map of url query parameters. + * Query parameters will be lazy parsed from url "tail" + * Please note: lazy parsing of query parameters is not thread-safe! + * @return map for "&key=value" + */ + const http::Protocol::QueryParams& getQueryParameters() const { + if(!m_queryParamsParsed) { + auto params = oatpp::network::Url::Parser::parseQueryParams(m_pathVariables.getTail()); + auto param = params->getFirstEntry(); + while(param != nullptr) { + m_queryParams[param->getKey()] = param->getValue(); + param = param->getNext(); + } + m_queryParamsParsed = true; + } + return m_queryParams; + } + const http::Protocol::Headers& getHeaders() const { return m_headers; } @@ -128,22 +137,18 @@ public: return m_pathVariables.getTail(); } - oatpp::String getQueryParameter(const std::string& key) const { - auto iter = m_queryParams->find(key); - if (iter == m_queryParams->end()) { + oatpp::String getQueryParameter(const oatpp::data::share::StringKeyLabel& name) const { + auto iter = getQueryParameters().find(name); + if (iter == getQueryParameters().end()) { return nullptr; } else { - return iter->second; + return iter->second.toString(); } } - oatpp::String getQueryParameter(std::string key, oatpp::String& _value) { - auto value = getQueryParameter(key); - return value == nullptr ? _value : value; - } - - std::shared_ptr> getQueryParameters() { - return m_queryParams; + oatpp::String getQueryParameter(const oatpp::data::share::StringKeyLabel& name, const oatpp::String& defaultValue) const { + auto value = getQueryParameter(name); + return value ? value : defaultValue; } void streamBody(const std::shared_ptr& toStream) const { diff --git a/src/oatpp/web/server/api/ApiController.hpp b/src/oatpp/web/server/api/ApiController.hpp index 3e2a1d84..d5a10258 100644 --- a/src/oatpp/web/server/api/ApiController.hpp +++ b/src/oatpp/web/server/api/ApiController.hpp @@ -52,9 +52,9 @@ public: typedef oatpp::web::protocol::http::outgoing::Response OutgoingResponse; typedef oatpp::web::protocol::http::Status Status; typedef oatpp::web::protocol::http::Header Header; + typedef oatpp::web::protocol::http::Protocol::QueryParams QueryParams; typedef oatpp::web::server::api::Endpoint Endpoint; typedef oatpp::collection::LinkedList> Endpoints; - typedef std::shared_ptr> QueryParameters; public: typedef oatpp::data::mapping::ObjectMapper ObjectMapper; diff --git a/test/oatpp/web/FullTest.cpp b/test/oatpp/web/FullTest.cpp index 02add27f..3546f662 100644 --- a/test/oatpp/web/FullTest.cpp +++ b/test/oatpp/web/FullTest.cpp @@ -133,7 +133,7 @@ void FullTest::onRun() { auto response = client->getWithQueries("oatpp", 1); auto dto = response->readBodyToDto(objectMapper); OATPP_ASSERT(dto); - OATPP_ASSERT(dto->testValue == "age=1&name=oatpp"); + OATPP_ASSERT(dto->testValue == "name=oatpp&age=1"); } { // test GET with header parameter diff --git a/test/oatpp/web/app/Controller.hpp b/test/oatpp/web/app/Controller.hpp index 398fe546..eb22473f 100644 --- a/test/oatpp/web/app/Controller.hpp +++ b/test/oatpp/web/app/Controller.hpp @@ -25,13 +25,15 @@ #ifndef oatpp_test_web_app_Controller_hpp #define oatpp_test_web_app_Controller_hpp -#include #include "./DTOs.hpp" #include "oatpp/web/server/api/ApiController.hpp" #include "oatpp/parser/json/mapping/ObjectMapper.hpp" +#include "oatpp/core/utils/ConversionUtils.hpp" #include "oatpp/core/macro/codegen.hpp" #include "oatpp/core/macro/component.hpp" +#include + namespace oatpp { namespace test { namespace web { namespace app { class Controller : public oatpp::web::server::api::ApiController { @@ -62,19 +64,21 @@ public: return createDtoResponse(Status::CODE_200, dto); } - ENDPOINT("GET", "queries/*", getWithQueries, - QUERIES(QueryParameters, queries), QUERY(String, name), QUERY(Int32, age)) { + ENDPOINT("GET", "queries", getWithQueries, + QUERIES(QueryParams, queries), QUERY(String, name), QUERY(Int32, age)) { std::ostringstream builder; - for (auto i = queries->begin(); i != queries->end(); ++i) { - if (i != queries->begin()) { + for (auto i = queries.begin(); i != queries.end(); ++i) { + if (i != queries.begin()) { builder << "&"; } - builder << i->first << "=" << i->second->std_str(); + builder << i->first.toStdString() << "=" << i->second.toStdString(); } auto queryString = builder.str(); OATPP_LOGD(TAG, "GET queries?%s =>(name=%s, age=%d)", queryString.c_str(), name->c_str(), age->getValue()); auto dto = TestDto::createShared(); - dto->testValue = queryString.c_str(); + + // return ordered key-value string instead of unordered from builder + dto->testValue = "name=" + name + "&age=" + oatpp::utils::conversion::int32ToStr(age->getValue()); return createDtoResponse(Status::CODE_200, dto); }