diff --git a/examples/helloworld/main.cc b/examples/helloworld/main.cc index 586cdd52..7b92827b 100644 --- a/examples/helloworld/main.cc +++ b/examples/helloworld/main.cc @@ -52,11 +52,11 @@ int main() [](const HttpRequestPtr &req, std::function &&callback) { auto resp = HttpResponse::newHttpResponse(); - std::string name = req->getParameter("user"); - if (name == "") + auto name = req->getOptionalParameter("user"); + if (!name) resp->setBody("Please tell me your name"); else - resp->setBody("Hello, " + name + "!"); + resp->setBody("Hello, " + name.value() + "!"); callback(resp); }, {Get}); diff --git a/lib/inc/drogon/HttpBinder.h b/lib/inc/drogon/HttpBinder.h index e681d9c1..107f0b56 100644 --- a/lib/inc/drogon/HttpBinder.h +++ b/lib/inc/drogon/HttpBinder.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -159,27 +160,8 @@ class HttpBinder : public HttpBinderBase std::string handlerName_; template - struct CanConvertFromStringStream - { - private: - using yes = std::true_type; - using no = std::false_type; - - template - static auto test(U *p, std::stringstream &&ss) - -> decltype((ss >> *p), yes()); - - template - static no test(...); - - public: - static constexpr bool value = - std::is_same(nullptr, std::stringstream())), - yes>::value; - }; - - template - typename std::enable_if::value, void>::type + typename std::enable_if::value, + void>::type getHandlerArgumentValue(T &value, std::string &&p) { if (!p.empty()) @@ -190,7 +172,8 @@ class HttpBinder : public HttpBinderBase } template - typename std::enable_if::value), void>::type + typename std::enable_if::value), + void>::type getHandlerArgumentValue(T &value, std::string &&p) { } diff --git a/lib/inc/drogon/HttpRequest.h b/lib/inc/drogon/HttpRequest.h index 2de7b95a..3ad1a163 100644 --- a/lib/inc/drogon/HttpRequest.h +++ b/lib/inc/drogon/HttpRequest.h @@ -16,6 +16,8 @@ #include #include +#include +#include #include #include #include @@ -277,6 +279,38 @@ class DROGON_EXPORT HttpRequest /// Get a parameter identified by the @param key virtual const std::string &getParameter(const std::string &key) const = 0; + /** + * @brief Get the optional parameter identified by the @param key. if the + * parameter doesn't exist, or the original parameter can't be converted to + * a T type object, an empty optional object is returned. + * + * @tparam T + * @param key + * @return optional + */ + template + optional getOptionalParameter(const std::string &key) + { + auto ¶ms = getParameters(); + auto it = params.find(key); + if (it != params.end()) + { + try + { + return optional(drogon::utils::fromString(it->second)); + } + catch (const std::exception &e) + { + LOG_ERROR << e.what(); + return optional{}; + } + } + else + { + return optional{}; + } + } + /// Return the remote IP address and port virtual const trantor::InetAddress &peerAddr() const = 0; const trantor::InetAddress &getPeerAddr() const diff --git a/lib/inc/drogon/utils/Utilities.h b/lib/inc/drogon/utils/Utilities.h index dc7099ec..0c7e0451 100644 --- a/lib/inc/drogon/utils/Utilities.h +++ b/lib/inc/drogon/utils/Utilities.h @@ -24,6 +24,8 @@ #include #include #include +#include +#include #ifdef _WIN32 #include char *strptime(const char *s, const char *f, struct tm *tm); @@ -31,6 +33,28 @@ time_t timegm(struct tm *tm); #endif namespace drogon { +namespace internal +{ +template +struct CanConvertFromStringStream +{ + private: + using yes = std::true_type; + using no = std::false_type; + + template + static auto test(U *p, std::stringstream &&ss) + -> decltype((ss >> *p), yes()); + + template + static no test(...); + + public: + static constexpr bool value = + std::is_same(nullptr, std::stringstream())), + yes>::value; +}; +} // namespace internal namespace utils { /// Determine if the string is an integer @@ -282,5 +306,105 @@ DROGON_EXPORT void replaceAll(std::string &s, */ DROGON_EXPORT bool secureRandomBytes(void *ptr, size_t size); +template +typename std::enable_if::value, T>::type +fromString(const std::string &p) noexcept(false) +{ + T value{}; + if (!p.empty()) + { + std::stringstream ss(p); + ss >> value; + } + return value; +} + +template +typename std::enable_if::value), + T>::type +fromString(const std::string &) noexcept(false) +{ + throw std::runtime_error("Bad type conversion"); +} + +template <> +inline std::string fromString(const std::string &p) noexcept(false) +{ + return p; +} + +template <> +inline int fromString(const std::string &p) noexcept(false) +{ + return std::stoi(p); +} + +template <> +inline long fromString(const std::string &p) noexcept(false) +{ + return std::stol(p); +} + +template <> +inline long long fromString(const std::string &p) noexcept(false) +{ + return std::stoll(p); +} + +template <> +inline unsigned long fromString(const std::string &p) noexcept( + false) +{ + return std::stoul(p); +} + +template <> +inline unsigned long long fromString( + const std::string &p) noexcept(false) +{ + return std::stoull(p); +} + +template <> +inline float fromString(const std::string &p) noexcept(false) +{ + return std::stof(p); +} + +template <> +inline double fromString(const std::string &p) noexcept(false) +{ + return std::stod(p); +} + +template <> +inline long double fromString(const std::string &p) noexcept(false) +{ + return std::stold(p); +} + +template <> +inline bool fromString(const std::string &p) noexcept(false) +{ + if (p == "1") + { + return true; + } + if (p == "0") + { + return false; + } + std::string l{p}; + std::transform(p.begin(), p.end(), l.begin(), tolower); + if (l == "true") + { + return true; + } + else if (l == "false") + { + return false; + } + throw std::runtime_error("Can't convert from string '" + p + "' to bool"); +} } // namespace utils } // namespace drogon