From 1414704b44d7513e948f93696965922a97aa0e1b Mon Sep 17 00:00:00 2001 From: Armstrong Date: Sat, 28 Sep 2019 21:48:06 +0800 Subject: [PATCH] add per request attribute store (#259) --- CMakeLists.txt | 1 + examples/simple_example/api_v1_ApiTest.cc | 32 ++++++ examples/simple_example/api_v1_ApiTest.h | 4 + examples/simple_example_test/main.cc | 27 +++++ lib/inc/drogon/Attribute.h | 132 ++++++++++++++++++++++ lib/inc/drogon/HttpRequest.h | 10 ++ lib/src/HttpRequestImpl.h | 11 ++ 7 files changed, 217 insertions(+) create mode 100644 lib/inc/drogon/Attribute.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 487c3a06..ce025f79 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -355,6 +355,7 @@ install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" COMPONENT lib) set(DROGON_HEADERS + lib/inc/drogon/Attribute.h lib/inc/drogon/CacheMap.h lib/inc/drogon/Cookie.h lib/inc/drogon/DrClassMap.h diff --git a/examples/simple_example/api_v1_ApiTest.cc b/examples/simple_example/api_v1_ApiTest.cc index 1f32762a..46a9dd65 100644 --- a/examples/simple_example/api_v1_ApiTest.cc +++ b/examples/simple_example/api_v1_ApiTest.cc @@ -412,3 +412,35 @@ void ApiTest::formTest(const HttpRequestPtr &req, auto resp = HttpResponse::newHttpJsonResponse(ret); callback(resp); } + +void ApiTest::attributesTest( + const HttpRequestPtr &req, + std::function &&callback) +{ + AttributesPtr attributes = req->getAttributes(); + const std::string key = "ATTR_ADDR"; + Json::Value ret; + + uint64_t data = (uint64_t)req.get(); + + if (attributes->find(key)) + { + ret["result"] = "bad"; + callback(HttpResponse::newHttpJsonResponse(ret)); + return; + } + + attributes->insert(key, data); + + if (!attributes->find(key) || attributes->get(key) != data) + { + ret["result"] = "bad"; + } + else + { + ret["result"] = "ok"; + } + + callback(HttpResponse::newHttpJsonResponse(ret)); + return; +} \ No newline at end of file diff --git a/examples/simple_example/api_v1_ApiTest.h b/examples/simple_example/api_v1_ApiTest.h index 2e72c60e..4ff50b60 100644 --- a/examples/simple_example/api_v1_ApiTest.h +++ b/examples/simple_example/api_v1_ApiTest.h @@ -33,6 +33,7 @@ class ApiTest : public drogon::HttpController Get); // path is /absolute/{arg1} METHOD_ADD(ApiTest::jsonTest, "/json", Post); METHOD_ADD(ApiTest::formTest, "/form", Post); + METHOD_ADD(ApiTest::attributesTest, "/attrs", Get); METHOD_LIST_END void get(const HttpRequestPtr &req, @@ -57,6 +58,9 @@ class ApiTest : public drogon::HttpController std::function &&callback); void formTest(const HttpRequestPtr &req, std::function &&callback); + void attributesTest( + const HttpRequestPtr &req, + std::function &&callback); public: ApiTest() diff --git a/examples/simple_example_test/main.cc b/examples/simple_example_test/main.cc index f190f0c9..cc7e82e7 100644 --- a/examples/simple_example_test/main.cc +++ b/examples/simple_example_test/main.cc @@ -925,6 +925,33 @@ void doTest(const HttpClientPtr &client, } }); + /// Test attributes + req = HttpRequest::newHttpRequest(); + req->setMethod(drogon::Get); + req->setPath("/api/v1/apitest/attrs"); + client->sendRequest(req, + [=](ReqResult result, const HttpResponsePtr &resp) { + if (result == ReqResult::Ok) + { + auto ret = resp->getJsonObject(); + if (ret && (*ret)["result"].asString() == "ok") + { + outputGood(req, isHttps); + } + else + { + LOG_DEBUG << resp->getBody(); + LOG_ERROR << "Error!"; + exit(1); + } + } + else + { + LOG_ERROR << "Error!"; + exit(1); + } + }); + /// Test attachment download req = HttpRequest::newHttpRequest(); req->setMethod(drogon::Get); diff --git a/lib/inc/drogon/Attribute.h b/lib/inc/drogon/Attribute.h new file mode 100644 index 00000000..17cfdd23 --- /dev/null +++ b/lib/inc/drogon/Attribute.h @@ -0,0 +1,132 @@ +/** + * + * Attribute.h + * armstrong@sweelia.com + * + * Copyright 2018, An Tao. All rights reserved. + * https://github.com/an-tao/drogon + * Use of this source code is governed by a MIT license + * that can be found in the License file. + * + * Drogon + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace drogon +{ +/** + * @brief This class represents a attribute stored in the request context. + * One can get or set any type of data to a attribute object. + */ +class Attributes +{ + public: + /** + * @brief Get the data identified by the key parameter. + * @note if the data is not found, a default value is returned. + * For example: + * @code + auto &userName = attributesPtr->get("user name"); + @endcode + */ + template + const T &get(const std::string &key) const + { + const static T nullVal = T(); + auto it = _attributesMap.find(key); + if (it != _attributesMap.end()) + { + if (typeid(T) == it->second.type()) + { + return *(any_cast(&(it->second))); + } + else + { + LOG_ERROR << "Bad type"; + } + } + return nullVal; + } + + /** + * @brief Get the 'any' object identified by the given key + */ + any &operator[](const std::string &key) + { + return _attributesMap[key]; + } + + /** + * @brief Insert a key-value pair + * @note here the any object can be created implicitly. for example + * @code + attributesPtr->insert("user name", userNameString); + @endcode + */ + void insert(const std::string &key, const any &obj) + { + _attributesMap[key] = obj; + } + + /** + * @brief Insert a key-value pair + * @note here the any object can be created implicitly. for example + * @code + attributesPtr->insert("user name", userNameString); + @endcode + */ + void insert(const std::string &key, any &&obj) + { + _attributesMap[key] = std::move(obj); + } + + /** + * @brief Erase the data identified by the given key. + */ + void erase(const std::string &key) + { + _attributesMap.erase(key); + } + + /** + * @brief Retrun true if the data identified by the key exists. + */ + bool find(const std::string &key) + { + if (_attributesMap.find(key) == _attributesMap.end()) + { + return false; + } + return true; + } + + /** + * @brief Clear all attributes. + */ + void clear() + { + _attributesMap.clear(); + } + + /** + * @brief Constructor, usually called by the framework + */ + Attributes() + { + } + + private: + typedef std::map AttributesMap; + AttributesMap _attributesMap; +}; + +typedef std::shared_ptr AttributesPtr; + +} // namespace drogon diff --git a/lib/inc/drogon/HttpRequest.h b/lib/inc/drogon/HttpRequest.h index f1f456d7..f3c4dbee 100644 --- a/lib/inc/drogon/HttpRequest.h +++ b/lib/inc/drogon/HttpRequest.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -167,6 +168,15 @@ class HttpRequest return session(); } + /// Get the attributes store to which the request belongs. + virtual AttributesPtr attributes() const = 0; + + /// Get the attributes store to which the request belongs. + AttributesPtr getAttributes() const + { + return attributes(); + } + /// Get parameters of the request. virtual const std::unordered_map ¶meters() const = 0; diff --git a/lib/src/HttpRequestImpl.h b/lib/src/HttpRequestImpl.h index de5f5b67..635b04ae 100644 --- a/lib/src/HttpRequestImpl.h +++ b/lib/src/HttpRequestImpl.h @@ -60,6 +60,7 @@ class HttpRequestImpl : public HttpRequest _parameters.clear(); _jsonPtr.reset(); _sessionPtr.reset(); + _attributesPtr.reset(); _cacheFilePtr.reset(); _expect.clear(); _content.clear(); @@ -340,6 +341,15 @@ class HttpRequestImpl : public HttpRequest _sessionPtr = session; } + virtual AttributesPtr attributes() const override + { + if (!_attributesPtr) + { + _attributesPtr = std::make_shared(); + } + return _attributesPtr; + } + virtual const std::shared_ptr jsonObject() const override { parseParametersOnce(); @@ -422,6 +432,7 @@ class HttpRequestImpl : public HttpRequest mutable std::unordered_map _parameters; mutable std::shared_ptr _jsonPtr; SessionPtr _sessionPtr; + mutable AttributesPtr _attributesPtr; trantor::InetAddress _peer; trantor::InetAddress _local; trantor::Date _date;