44#include < iostream>
55#include < memory>
66#include < string>
7+ #include < type_traits>
78#include < utility>
89#include < vector>
910
@@ -45,24 +46,43 @@ concept JSON =
4546 boost::json::has_value_from<T>::value &&
4647 !std::is_same_v<T, std::string>; // avoid ambiguity with string handlers
4748
49+ template <typename T>
50+ concept StatusResponse =
51+ std::is_same_v<typename T::first_type, boost::beast::http::status>;
52+
53+ template <typename T>
54+ concept ContentOnlyResponse = !StatusResponse<T>;
55+
56+ template <typename Fn>
57+ concept ContentOnlyHandler = requires (Fn f, utl::first_argument<Fn> arg) {
58+ { f (arg) } -> std::same_as<std::string>;
59+ } || requires (Fn f, utl::first_argument<Fn> arg) {
60+ { f (arg) } -> ContentOnlyResponse; // avoid ambiguity with json handlers
61+ { f (arg) } -> JSON;
62+ };
63+
4864template <typename Fn>
4965concept StringGetHandler = requires (boost::urls::url_view const & url, Fn f) {
50- { f (url) } -> std::same_as<std::string>;
66+ { f (url) } -> StatusResponse;
67+ { f (url).second } -> std::same_as<std::string>;
5168};
5269
5370template <typename Fn>
5471concept StringPostHandler = requires (std::string_view const & req, Fn f) {
55- { f (req) } -> std::same_as<std::string>;
72+ { f (req) } -> StatusResponse;
73+ { f (req).second } -> std::same_as<std::string>;
5674};
5775
5876template <typename Fn>
59- concept JsonPostHandler = requires (Fn f, typename utl::first_argument<Fn> arg) {
60- { f (arg) } -> JSON;
77+ concept JsonPostHandler = requires (Fn f, utl::first_argument<Fn> arg) {
78+ { f (arg) } -> StatusResponse;
79+ { f (arg).second } -> JSON;
6180};
6281
6382template <typename Fn>
6483concept JsonGetHandler = requires (boost::urls::url_view const & url, Fn f) {
65- { f (url) } -> JSON;
84+ { f (url) } -> StatusResponse;
85+ { f (url).second } -> JSON;
6686};
6787
6888struct default_exec {
@@ -169,7 +189,10 @@ struct query_router {
169189 return *this ;
170190 }
171191
172- template <StringPostHandler Fn>
192+ template <typename Fn>
193+ requires requires (std::string_view const & req, Fn f) {
194+ { f (req) } -> std::same_as<std::string>;
195+ }
173196 query_router& route (std::string method, std::string const & path_regex,
174197 Fn&& fn) {
175198 return route (std::move (method), path_regex,
@@ -183,19 +206,47 @@ struct query_router {
183206 });
184207 }
185208
209+ template <ContentOnlyHandler Fn>
210+ query_router& get (std::string const & path_regex, Fn&& fn) {
211+ return get (path_regex,
212+ [fn = std::forward<Fn>(fn)](boost::urls::url_view const & url) {
213+ return std::make_pair (boost::beast::http::status::ok, fn (url));
214+ });
215+ }
216+
217+ template <ContentOnlyHandler Fn>
218+ query_router& post (std::string const & path_regex, Fn&& fn) {
219+ return post (path_regex, [fn = std::forward<Fn>(fn)](
220+ typename utl::first_argument<Fn> arg) {
221+ return std::make_pair (boost::beast::http::status::ok, fn (arg));
222+ });
223+ }
224+
225+ template <StringPostHandler Fn>
226+ query_router& post (std::string const & path_regex, Fn&& fn) {
227+ return route (" POST" , path_regex,
228+ [fn = std::forward<Fn>(fn)](web_server::http_req_t const & req,
229+ bool is_ssl) {
230+ auto [status, content] = fn (req.body ());
231+ auto res =
232+ net::web_server::string_res_t {status, req.version ()};
233+ set_response_body (res, req, content);
234+ res.keep_alive (req.keep_alive ());
235+ return res;
236+ });
237+ }
238+
186239 template <StringGetHandler Fn>
187240 query_router& get (std::string const & path_regex, Fn&& fn) {
188- namespace http = boost::beast::http;
189- namespace json = boost::json;
190- return route (
191- " GET" , path_regex,
192- [fn = std::forward<Fn>(fn)](route_request const & req,
193- bool const ssl) -> reply {
194- auto res = web_server::string_res_t {http::status::ok, req.version ()};
195- set_response_body (res, req, fn (boost::url_view{req.target ()}));
196- res.keep_alive (req.keep_alive ());
197- return res;
198- });
241+ return route (" GET" , path_regex,
242+ [fn = std::forward<Fn>(fn)](route_request const & req, bool ) {
243+ auto [status, content] = fn (boost::url_view{req.target ()});
244+
245+ auto res = web_server::string_res_t {status, req.version ()};
246+ set_response_body (res, req, content);
247+ res.keep_alive (req.keep_alive ());
248+ return res;
249+ });
199250 }
200251
201252 template <JsonPostHandler Fn>
@@ -204,32 +255,31 @@ struct query_router {
204255 " POST" , path_regex,
205256 [fn = std::forward<Fn>(fn)](web_server::http_req_t const & req,
206257 bool is_ssl) -> reply {
207- auto res = net::web_server::string_res_t {
208- boost::beast::http::status::ok, req.version ()};
258+ auto [status, content] =
259+ fn (boost::json::value_to<std::decay_t <utl::first_argument<Fn>>>(
260+ boost::json::parse (req.body ())));
261+ auto res = net::web_server::string_res_t {status, req.version ()};
209262 res.set (boost::beast::http::field::content_type, " application/json" );
210263 set_response_body (
211264 res, req,
212- boost::json::serialize (boost::json::value_from (fn (
213- boost::json::value_to<std::decay_t <utl::first_argument<Fn>>>(
214- boost::json::parse (req.body ()))))));
265+ boost::json::serialize (boost::json::value_from (content)));
215266 res.keep_alive (req.keep_alive ());
216267 return res;
217268 });
218269 }
219270
220271 template <JsonGetHandler Fn>
221272 query_router& get (std::string const & path_regex, Fn&& fn) {
222- namespace http = boost::beast::http;
223273 namespace json = boost::json;
224274 return route (" GET" , path_regex,
225- [fn = std::forward<Fn>(fn)](route_request const & req,
226- bool const ssl) -> reply {
227- auto res = web_server:: string_res_t {http::status::ok,
228- req.version ()};
229- res.set (http::field::content_type, " application/json " );
230- set_response_body (res, req,
231- json::serialize ( json::value_from (
232- fn (boost::url_view{req. target ()}) )));
275+ [fn = std::forward<Fn>(fn)](route_request const & req, bool ) {
276+ auto [status, content] = fn (boost::url_view{req. target ()});
277+
278+ auto res = web_server:: string_res_t {status, req.version ()};
279+ res.set (boost::beast:: http::field::content_type,
280+ " application/json " );
281+ set_response_body (
282+ res, req, json::serialize ( json::value_from (content )));
233283 res.keep_alive (req.keep_alive ());
234284 return res;
235285 });
0 commit comments