From e7b6ba27bb82b7b9ff8e2a0301e520678fe33d60 Mon Sep 17 00:00:00 2001 From: An Tao Date: Sat, 19 Oct 2019 14:27:34 +0800 Subject: [PATCH] Make user can use a custom type parameter instead of the first parameter in handlers of HttpControllers (#284) --- README.md | 8 +-- README.zh-CN.md | 2 +- examples/simple_example/CustomCtrl.h | 2 +- examples/simple_example/api_v1_ApiTest.cc | 5 +- examples/simple_example/api_v1_ApiTest.h | 11 ++-- examples/simple_example/main.cc | 13 +++-- lib/inc/drogon/HttpBinder.h | 68 ++++++++++++++++++++--- lib/inc/drogon/utils/FunctionTraits.h | 33 +++++++++++ 8 files changed, 112 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 93684031..7e623919 100755 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ int main() Drogon provides some interfaces for adding controller logic directly in the main() function, for example, user can register a handler like this in Drogon: ```c++ -app.registerHandler("/test?username={1}", +app.registerHandler("/test?username={name}", [](const HttpRequestPtr& req, std::function &&callback, const std::string &name) @@ -158,9 +158,9 @@ class User : public drogon::HttpController public: METHOD_LIST_BEGIN //use METHOD_ADD to add your custom processing function here; - METHOD_ADD(User::getInfo, "/{1}", Get); //path is /api/v1/User/{arg1} - METHOD_ADD(User::getDetailInfo, "/{1}/detailinfo", Get); //path is /api/v1/User/{arg1}/detailinfo - METHOD_ADD(User::newUser, "/{1}", Post); //path is /api/v1/User/{arg1} + METHOD_ADD(User::getInfo, "/{id}", Get); //path is /api/v1/User/{arg1} + METHOD_ADD(User::getDetailInfo, "/{id}/detailinfo", Get); //path is /api/v1/User/{arg1}/detailinfo + METHOD_ADD(User::newUser, "/{name}", Post); //path is /api/v1/User/{arg1} METHOD_LIST_END //your declaration of processing function maybe like this: void getInfo(const HttpRequestPtr &req, std::function &&callback, int userId) const; diff --git a/README.zh-CN.md b/README.zh-CN.md index a51b6d21..090eb6e6 100755 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -11,7 +11,7 @@ Drogon的主要应用平台是Linux,也支持Mac OS、FreeBSD,目前还不支持Windows。它的主要特点如下: -* 网络层使用基于epoll(MacOS/FreeBSD下是kqueue)的非阻塞IO框架,提供高并发、高性能的网络IO。详细请见[性能测试](https://github.com/an-tao/drogon/wiki/13-%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95); +* 网络层使用基于epoll(MacOS/FreeBSD下是kqueue)的非阻塞IO框架,提供高并发、高性能的网络IO。详细请见[性能测试](https://github.com/an-tao/drogon/wiki/13-%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95)和[TFB Live Results](https://tfb-status.techempower.com/); * 全异步编程模式; * 支持Http1.0/1.1(server端和client端); * 基于template实现了简单的反射机制,使主程序框架、控制器(controller)和视图(view)完全解耦; diff --git a/examples/simple_example/CustomCtrl.h b/examples/simple_example/CustomCtrl.h index 4d1b6d92..436d11fd 100644 --- a/examples/simple_example/CustomCtrl.h +++ b/examples/simple_example/CustomCtrl.h @@ -7,7 +7,7 @@ class CustomCtrl : public drogon::HttpController METHOD_LIST_BEGIN // use METHOD_ADD to add your custom processing function here; METHOD_ADD(CustomCtrl::hello, - "/{1}", + "/{userName}", Get, "CustomHeaderFilter"); // path is /customctrl/{arg1} METHOD_LIST_END diff --git a/examples/simple_example/api_v1_ApiTest.cc b/examples/simple_example/api_v1_ApiTest.cc index 0d5f89e4..614e0fdc 100644 --- a/examples/simple_example/api_v1_ApiTest.cc +++ b/examples/simple_example/api_v1_ApiTest.cc @@ -374,9 +374,8 @@ void ApiTest::get2(const HttpRequestPtr &req, callback(res); } -void ApiTest::jsonTest(const HttpRequestPtr &req, - std::function &&callback, - std::shared_ptr &&json) +void ApiTest::jsonTest(std::shared_ptr &&json, + std::function &&callback) { Json::Value ret; if (json) diff --git a/examples/simple_example/api_v1_ApiTest.h b/examples/simple_example/api_v1_ApiTest.h index 16591805..e748606b 100644 --- a/examples/simple_example/api_v1_ApiTest.h +++ b/examples/simple_example/api_v1_ApiTest.h @@ -18,7 +18,7 @@ class ApiTest : public drogon::HttpController "drogon::IntranetIpFilter"); METHOD_ADD(ApiTest::rootPost, "", Post, Options); METHOD_ADD(ApiTest::get, - "/get/{2:p2}/{1}", + "/get/{2:p2}/{1:p1}", Get); // path is /api/v1/apitest/get/{arg2}/{arg1} METHOD_ADD(ApiTest::your_method_name, "/{PI}/List?P2={}", @@ -26,10 +26,10 @@ class ApiTest : public drogon::HttpController METHOD_ADD(ApiTest::staticApi, "/static", Get, Options); // CORS METHOD_ADD(ApiTest::staticApi, "/static", Post, Put, Delete); METHOD_ADD(ApiTest::get2, - "/get/{1}", + "/get/{}", Get); // path is /api/v1/apitest/get/{arg1} ADD_METHOD_TO(ApiTest::get2, - "/absolute/{1}", + "/absolute/{}", Get); // path is /absolute/{arg1} METHOD_ADD(ApiTest::jsonTest, "/json", Post); METHOD_ADD(ApiTest::formTest, "/form", Post); @@ -54,9 +54,8 @@ class ApiTest : public drogon::HttpController std::function &&callback); void rootPost(const HttpRequestPtr &req, std::function &&callback); - void jsonTest(const HttpRequestPtr &req, - std::function &&callback, - std::shared_ptr &&json); + void jsonTest(std::shared_ptr &&json, + std::function &&callback); void formTest(const HttpRequestPtr &req, std::function &&callback); void attributesTest( diff --git a/examples/simple_example/main.cc b/examples/simple_example/main.cc index 2d253401..15b3ac67 100644 --- a/examples/simple_example/main.cc +++ b/examples/simple_example/main.cc @@ -122,7 +122,7 @@ using namespace drogon; namespace drogon { template <> -string_view fromRequest(const HttpRequest &req) +string_view fromRequest(const HttpRequest &req) { return req.body(); } @@ -143,12 +143,13 @@ int main() .addListener("0.0.0.0", 8849, true); } // Class function example - app().registerHandler("/api/v1/handle1/{1}/{2}/?p3={3}&p4={4}", &A::handle); - app().registerHandler("/api/v1/handle11/{1}/{2}/?p3={3}&p4={4}", - &A::staticHandle); + app().registerHandler("/api/v1/handle1/{}/{}/?p3={}&p4={}", &A::handle); + app().registerHandler( + "/api/v1/handle11/{int p1}/{string p2}/?p3={string p3}&p4={int p4}", + &A::staticHandle); // Lambda example app().registerHandler( - "/api/v1/handle2/{1}/{2}", + "/api/v1/handle2/{int a}/{float b}", [](const HttpRequestPtr &req, std::function &&callback, int a, // here the `a` parameter is converted from the number 1 @@ -186,7 +187,7 @@ int main() const std::string &, int)> func = std::bind(&A::handle, &tmp, _1, _2, _3, _4, _5, _6); - app().registerHandler("/api/v1/handle4/{4}/{3}/{1}", func); + app().registerHandler("/api/v1/handle4/{4:p4}/{3:p3}/{1:p1}", func); app().setDocumentRoot("./"); app().enableSession(60); diff --git a/lib/inc/drogon/HttpBinder.h b/lib/inc/drogon/HttpBinder.h index 0916a280..b23d9004 100644 --- a/lib/inc/drogon/HttpBinder.h +++ b/lib/inc/drogon/HttpBinder.h @@ -273,8 +273,11 @@ class HttpBinder : public HttpBinderBase } template - typename std::enable_if::type + bool isDrObjectClass = traits::isDrObjectClass, + bool isNormal = std::is_same::value> + typename std::enable_if::type callFunction(const HttpRequestPtr &req, std::function &&callback, Values &&... values) @@ -284,8 +287,11 @@ class HttpBinder : public HttpBinderBase } template - typename std::enable_if::type + bool isDrObjectClass = traits::isDrObjectClass, + bool isNormal = std::is_same::value> + typename std::enable_if::type callFunction(const HttpRequestPtr &req, std::function &&callback, Values &&... values) @@ -295,14 +301,58 @@ class HttpBinder : public HttpBinderBase (*objPtr.*_func)(req, std::move(callback), std::move(values)...); } template - typename std::enable_if::type callFunction( - const HttpRequestPtr &req, - std::function &&callback, - Values &&... values) + bool isClassFunction = traits::isClassFunction, + bool isNormal = std::is_same::value> + typename std::enable_if::type + callFunction(const HttpRequestPtr &req, + std::function &&callback, + Values &&... values) { _func(req, std::move(callback), std::move(values)...); } + //////////////////////////////////////////////////////////////// + template ::value> + typename std::enable_if::type + callFunction(const HttpRequestPtr &req, + std::function &&callback, + Values &&... values) + { + static auto &obj = getControllerObj(); + (obj.*_func)((*req), std::move(callback), std::move(values)...); + } + template ::value> + typename std::enable_if::type + callFunction(const HttpRequestPtr &req, + std::function &&callback, + Values &&... values) + { + static auto objPtr = + DrClassMap::getSingleInstance(); + (*objPtr.*_func)((*req), std::move(callback), std::move(values)...); + } + template ::value> + typename std::enable_if::type + callFunction(const HttpRequestPtr &req, + std::function &&callback, + Values &&... values) + { + _func((*req), std::move(callback), std::move(values)...); + } + ///////////// }; } // namespace internal diff --git a/lib/inc/drogon/utils/FunctionTraits.h b/lib/inc/drogon/utils/FunctionTraits.h index a327157c..c68531e8 100644 --- a/lib/inc/drogon/utils/FunctionTraits.h +++ b/lib/inc/drogon/utils/FunctionTraits.h @@ -85,6 +85,39 @@ struct FunctionTraits< { static const bool isHTTPFunction = true; typedef void class_type; + typedef HttpRequestPtr first_param_type; +}; + +template +struct FunctionTraits< + ReturnType (*)(HttpRequestPtr &req, + std::function &&callback, + Arguments...)> : FunctionTraits +{ + static const bool isHTTPFunction = false; + typedef void class_type; +}; + +template +struct FunctionTraits< + ReturnType (*)(HttpRequestPtr &&req, + std::function &&callback, + Arguments...)> : FunctionTraits +{ + static const bool isHTTPFunction = false; + typedef void class_type; +}; + +// normal function for HTTP handling +template +struct FunctionTraits< + ReturnType (*)(T &&customReq, + std::function &&callback, + Arguments...)> : FunctionTraits +{ + static const bool isHTTPFunction = true; + typedef void class_type; + typedef T first_param_type; }; // normal function