Separate middleware for handlers

This commit is contained in:
Vladislav Oleshko 2022-02-01 16:31:05 +03:00
parent 9f64a7b667
commit e60714c0b2
10 changed files with 494 additions and 254 deletions

View File

@ -0,0 +1,55 @@
#include "crow.h"
struct RequestLogger
{
struct context
{};
void before_handle(crow::request& req, crow::response& /*res*/, context& /*ctx*/)
{
CROW_LOG_INFO << "Request to:" + req.url;
}
void after_handle(crow::request& /*req*/, crow::response& /*res*/, context& /*ctx*/)
{}
};
// Per handler middleware has to extend ILocalMiddleware
// It is called only if enabled
struct SecretContentGuard : crow::ILocalMiddleware
{
struct context
{};
void before_handle(crow::request& /*req*/, crow::response& res, context& /*ctx*/)
{
res.write("SECRET!");
res.code = 403;
res.end();
}
void after_handle(crow::request& /*req*/, crow::response& /*res*/, context& /*ctx*/)
{}
};
int main()
{
// ALL middleware (including per handler) is listed
crow::App<RequestLogger, SecretContentGuard> app;
CROW_ROUTE(app, "/")
([]() {
return "Hello, world!";
});
CROW_ROUTE(app, "/secret")
// Enable SecretContentGuard for this handler
.middlewares<decltype(app), SecretContentGuard>()
([]() {
return "";
});
app.port(18080).run();
return 0;
}

View File

@ -17,6 +17,7 @@
#include "crow/http_response.h"
#include "crow/multipart.h"
#include "crow/routing.h"
#include "crow/middleware.h"
#include "crow/middleware_context.h"
#include "crow/compression.h"
#include "crow/http_connection.h"

View File

@ -68,7 +68,7 @@ namespace crow
}
/// Process the request and generate a response for it
void handle(const request& req, response& res)
void handle(request& req, response& res)
{
router_.handle(req, res);
}
@ -394,6 +394,7 @@ namespace crow
// middleware
using context_t = detail::context<Middlewares...>;
using mw_container_t = std::tuple<Middlewares...>;
template<typename T>
typename T::context& get_context(const request& req)
{

View File

@ -15,6 +15,7 @@
#include "crow/settings.h"
#include "crow/task_timer.h"
#include "crow/middleware_context.h"
#include "crow/middleware.h"
#include "crow/socket_adaptors.h"
#include "crow/compression.h"
@ -23,150 +24,6 @@ namespace crow
using namespace boost;
using tcp = asio::ip::tcp;
namespace detail
{
template<typename MW>
struct check_before_handle_arity_3_const
{
template<typename T, void (T::*)(request&, response&, typename MW::context&) const = &T::before_handle>
struct get
{};
};
template<typename MW>
struct check_before_handle_arity_3
{
template<typename T, void (T::*)(request&, response&, typename MW::context&) = &T::before_handle>
struct get
{};
};
template<typename MW>
struct check_after_handle_arity_3_const
{
template<typename T, void (T::*)(request&, response&, typename MW::context&) const = &T::after_handle>
struct get
{};
};
template<typename MW>
struct check_after_handle_arity_3
{
template<typename T, void (T::*)(request&, response&, typename MW::context&) = &T::after_handle>
struct get
{};
};
template<typename T>
struct is_before_handle_arity_3_impl
{
template<typename C>
static std::true_type f(typename check_before_handle_arity_3_const<T>::template get<C>*);
template<typename C>
static std::true_type f(typename check_before_handle_arity_3<T>::template get<C>*);
template<typename C>
static std::false_type f(...);
public:
static const bool value = decltype(f<T>(nullptr))::value;
};
template<typename T>
struct is_after_handle_arity_3_impl
{
template<typename C>
static std::true_type f(typename check_after_handle_arity_3_const<T>::template get<C>*);
template<typename C>
static std::true_type f(typename check_after_handle_arity_3<T>::template get<C>*);
template<typename C>
static std::false_type f(...);
public:
static const bool value = decltype(f<T>(nullptr))::value;
};
template<typename MW, typename Context, typename ParentContext>
typename std::enable_if<!is_before_handle_arity_3_impl<MW>::value>::type
before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
{
mw.before_handle(req, res, ctx.template get<MW>(), ctx);
}
template<typename MW, typename Context, typename ParentContext>
typename std::enable_if<is_before_handle_arity_3_impl<MW>::value>::type
before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
{
mw.before_handle(req, res, ctx.template get<MW>());
}
template<typename MW, typename Context, typename ParentContext>
typename std::enable_if<!is_after_handle_arity_3_impl<MW>::value>::type
after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
{
mw.after_handle(req, res, ctx.template get<MW>(), ctx);
}
template<typename MW, typename Context, typename ParentContext>
typename std::enable_if<is_after_handle_arity_3_impl<MW>::value>::type
after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
{
mw.after_handle(req, res, ctx.template get<MW>());
}
template<int N, typename Context, typename Container, typename CurrentMW, typename... Middlewares>
bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx)
{
using parent_context_t = typename Context::template partial<N - 1>;
before_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
if (res.is_completed())
{
after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
return true;
}
if (middleware_call_helper<N + 1, Context, Container, Middlewares...>(middlewares, req, res, ctx))
{
after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
return true;
}
return false;
}
template<int N, typename Context, typename Container>
bool middleware_call_helper(Container& /*middlewares*/, request& /*req*/, response& /*res*/, Context& /*ctx*/)
{
return false;
}
template<int N, typename Context, typename Container>
typename std::enable_if<(N < 0)>::type
after_handlers_call_helper(Container& /*middlewares*/, Context& /*context*/, request& /*req*/, response& /*res*/)
{
}
template<int N, typename Context, typename Container>
typename std::enable_if<(N == 0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res)
{
using parent_context_t = typename Context::template partial<N - 1>;
using CurrentMW = typename std::tuple_element<N, typename std::remove_reference<Container>::type>::type;
after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
}
template<int N, typename Context, typename Container>
typename std::enable_if<(N > 0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res)
{
using parent_context_t = typename Context::template partial<N - 1>;
using CurrentMW = typename std::tuple_element<N, typename std::remove_reference<Container>::type>::type;
after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
after_handlers_call_helper<N - 1, Context, Container>(middlewares, ctx, req, res);
}
} // namespace detail
#ifdef CROW_ENABLE_DEBUG
static std::atomic<int> connectionCount;
@ -316,8 +173,11 @@ namespace crow
ctx_ = detail::context<Middlewares...>();
req.middleware_context = static_cast<void*>(&ctx_);
req.middleware_container = static_cast<void*>(middlewares_);
req.io_service = &adaptor_.get_io_service();
detail::middleware_call_helper<0, decltype(ctx_), decltype(*middlewares_), Middlewares...>(*middlewares_, req, res, ctx_);
detail::middleware_call_helper<detail::middleware_call_criteria_only_global,
0, decltype(ctx_), decltype(*middlewares_)>(*middlewares_, req, res, ctx_);
if (!res.completed_)
{
@ -351,6 +211,7 @@ namespace crow
// call all after_handler of middlewares
detail::after_handlers_call_helper<
detail::middleware_call_criteria_only_global,
(static_cast<int>(sizeof...(Middlewares)) - 1),
decltype(ctx_),
decltype(*middlewares_)>(*middlewares_, ctx_, req_, res);

View File

@ -34,6 +34,7 @@ namespace crow
std::string remote_ip_address; ///< The IP address from which the request was sent.
void* middleware_context{};
void* middleware_container{};
boost::asio::io_service* io_service{};
/// Construct an empty request. (sets the method to `GET`)

308
include/crow/middleware.h Normal file
View File

@ -0,0 +1,308 @@
#pragma once
#include "crow/http_request.h"
#include "crow/http_response.h"
#include "crow/utility.h"
#include <tuple>
#include <type_traits>
#include <iostream>
#include <utility>
namespace crow {
/// Local middleware should extend ILocalMiddleware
struct ILocalMiddleware {
using call_global = std::false_type;
};
namespace detail
{
template<typename MW>
struct check_before_handle_arity_3_const
{
template<typename T, void (T::*)(request&, response&, typename MW::context&) const = &T::before_handle>
struct get
{};
};
template<typename MW>
struct check_before_handle_arity_3
{
template<typename T, void (T::*)(request&, response&, typename MW::context&) = &T::before_handle>
struct get
{};
};
template<typename MW>
struct check_after_handle_arity_3_const
{
template<typename T, void (T::*)(request&, response&, typename MW::context&) const = &T::after_handle>
struct get
{};
};
template<typename MW>
struct check_after_handle_arity_3
{
template<typename T, void (T::*)(request&, response&, typename MW::context&) = &T::after_handle>
struct get
{};
};
template<typename MW>
struct check_global_call_false {
template<typename T, typename std::enable_if<T::call_global::value == false, bool>::type = true>
struct get
{};
};
template<typename T>
struct is_before_handle_arity_3_impl
{
template<typename C>
static std::true_type f(typename check_before_handle_arity_3_const<T>::template get<C>*);
template<typename C>
static std::true_type f(typename check_before_handle_arity_3<T>::template get<C>*);
template<typename C>
static std::false_type f(...);
public:
static const bool value = decltype(f<T>(nullptr))::value;
};
template<typename T>
struct is_after_handle_arity_3_impl
{
template<typename C>
static std::true_type f(typename check_after_handle_arity_3_const<T>::template get<C>*);
template<typename C>
static std::true_type f(typename check_after_handle_arity_3<T>::template get<C>*);
template<typename C>
static std::false_type f(...);
public:
static constexpr bool value = decltype(f<T>(nullptr))::value;
};
template<typename MW, typename Context, typename ParentContext>
typename std::enable_if<!is_before_handle_arity_3_impl<MW>::value>::type
before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
{
mw.before_handle(req, res, ctx.template get<MW>(), ctx);
}
template<typename MW, typename Context, typename ParentContext>
typename std::enable_if<is_before_handle_arity_3_impl<MW>::value>::type
before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
{
mw.before_handle(req, res, ctx.template get<MW>());
}
template<typename MW, typename Context, typename ParentContext>
typename std::enable_if<!is_after_handle_arity_3_impl<MW>::value>::type
after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
{
mw.after_handle(req, res, ctx.template get<MW>(), ctx);
}
template<typename MW, typename Context, typename ParentContext>
typename std::enable_if<is_after_handle_arity_3_impl<MW>::value>::type
after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
{
mw.after_handle(req, res, ctx.template get<MW>());
}
template<template<typename QueryMW> class CallCriteria, // Checks if QueryMW should be called in this context
int N, typename Context, typename Container>
typename std::enable_if<(N < std::tuple_size<typename std::remove_reference<Container>::type>::value), bool>::type
middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx)
{
using CurrentMW = typename std::tuple_element<N, typename std::remove_reference<Container>::type>::type;
if (!CallCriteria<CurrentMW>::value) {
return middleware_call_helper<CallCriteria, N + 1, Context, Container>(middlewares, req, res, ctx);
}
using parent_context_t = typename Context::template partial<N - 1>;
before_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
if (res.is_completed())
{
after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
return true;
}
if (middleware_call_helper<CallCriteria, N + 1, Context, Container>(middlewares, req, res, ctx))
{
after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
return true;
}
return false;
}
template<template<typename QueryMW> class CallCriteria, int N, typename Context, typename Container>
typename std::enable_if<(N >= std::tuple_size<typename std::remove_reference<Container>::type>::value), bool>::type
middleware_call_helper(Container& /*middlewares*/, request& /*req*/, response& /*res*/, Context& /*ctx*/)
{
return false;
}
template<template<typename QueryMW> class CallCriteria, int N, typename Context, typename Container>
typename std::enable_if<(N < 0)>::type
after_handlers_call_helper(Container& /*middlewares*/, Context& /*context*/, request& /*req*/, response& /*res*/)
{
}
template<template<typename QueryMW> class CallCriteria, int N, typename Context, typename Container>
typename std::enable_if<(N == 0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res)
{
using parent_context_t = typename Context::template partial<N - 1>;
using CurrentMW = typename std::tuple_element<N, typename std::remove_reference<Container>::type>::type;
if (CallCriteria<CurrentMW>::value) {
after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
}
}
template<template<typename QueryMW> class CallCriteria, int N, typename Context, typename Container>
typename std::enable_if<(N > 0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res)
{
using parent_context_t = typename Context::template partial<N - 1>;
using CurrentMW = typename std::tuple_element<N, typename std::remove_reference<Container>::type>::type;
if (CallCriteria<CurrentMW>::value) {
after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
}
after_handlers_call_helper<CallCriteria, N - 1, Context, Container>(middlewares, ctx, req, res);
}
// A CallCriteria that accepts only global middleware
template<typename MW>
struct middleware_call_criteria_only_global
{
template<typename C>
static std::false_type f(typename check_global_call_false<MW>::template get<C>*);
template<typename C>
static std::true_type f(...);
static const bool value = decltype(f<MW>(nullptr))::value;
};
// wrapped_handler_call transparently wraps a handler call behind (req, res, args...)
template<typename F, typename... Args>
typename std::enable_if<black_magic::is_callable<F, const crow::request, crow::response&, Args...>::value>::type
wrapped_handler_call(crow::request& req, crow::response& res, const F& f, Args&&... args)
{
static_assert(std::is_same<void, decltype(f(std::declval<crow::request>(), std::declval<crow::response&>(), std::declval<Args>()...))>::value,
"Handler function with response argument should have void return type");
f(req, res, std::forward<Args>(args)...);
}
template<typename F, typename... Args>
typename std::enable_if<black_magic::is_callable<F, crow::request&, crow::response&, Args...>::value
&& !black_magic::is_callable<F, const crow::request, crow::response&, Args...>::value
>::type
wrapped_handler_call(crow::request& req, crow::response& res, const F& f, Args&&... args)
{
static_assert(std::is_same<void, decltype(f(std::declval<crow::request&>(), std::declval<crow::response&>(), std::declval<Args>()...))>::value,
"Handler function with response argument should have void return type");
f(req, res, std::forward<Args>(args)...);
}
template<typename F, typename... Args>
typename std::enable_if<black_magic::is_callable<F, crow::response&, Args...>::value>::type
wrapped_handler_call(crow::request& /*req*/, crow::response& res, const F& f, Args&&... args)
{
static_assert(std::is_same<void, decltype(f(std::declval<crow::response&>(), std::declval<Args>()...))>::value,
"Handler function with response argument should have void return type");
f(res, std::forward<Args>(args)...);
}
template<typename F, typename... Args>
typename std::enable_if<black_magic::is_callable<F, crow::request, Args...>::value>::type
wrapped_handler_call(crow::request& req, crow::response& res, const F& f, Args&&... args)
{
static_assert(!std::is_same<void, decltype(f(std::declval<crow::request>(), std::declval<Args>()...))>::value,
"Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable");
res = crow::response(f(req, std::forward<Args>(args)...));
res.end();
}
template<typename F, typename... Args>
typename std::enable_if<black_magic::is_callable<F, Args...>::value>::type
wrapped_handler_call(crow::request& /*req*/, crow::response& res, const F& f, Args&&... args)
{
static_assert(!std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
"Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable");
res = crow::response(f(std::forward<Args>(args)...));
res.end();
}
template<typename F, typename App, typename... Middlewares>
struct handler_middleware_wrapper
{
// CallCriteria bound to the current Middlewares pack
template<typename MW>
struct middleware_call_criteria
{
static constexpr bool value = black_magic::has_type<MW, std::tuple<Middlewares...>>::value;
};
template<typename... Args>
void operator()(crow::request& req, crow::response& res, Args&&... args) const
{
auto& ctx = *reinterpret_cast<typename App::context_t*>(req.middleware_context);
auto& container = *reinterpret_cast<typename App::mw_container_t*>(req.middleware_container);
bool completed = middleware_call_helper<middleware_call_criteria,
0, typename App::context_t, typename App::mw_container_t>(container, req, res, ctx);
if (completed) return;
wrapped_handler_call(req, res, f, std::forward<Args>(args)...);
after_handlers_call_helper<
middleware_call_criteria,
std::tuple_size<typename App::mw_container_t>::value - 1,
typename App::context_t,
typename App::mw_container_t>(container, ctx, req, res);
}
F f;
};
template<typename Route, typename App, typename... Middlewares>
struct handler_call_bridge
{
template<typename MW>
using check_app_contains = typename black_magic::has_type<MW, typename App::mw_container_t>;
static_assert(black_magic::all_true<(std::is_base_of<crow::ILocalMiddleware, Middlewares>::value)...>::value,
"Local middleware has to inherit crow::ILocalMiddleware");
static_assert(black_magic::all_true<(check_app_contains<Middlewares>::value)...>::value,
"Local middleware has to be listed in app middleware");
template<typename F>
void operator()(F&& f) const
{
auto wrapped = handler_middleware_wrapper<F, App, Middlewares...> {std::forward<F>(f)};
tptr->operator()(std::move(wrapped));
}
Route* tptr;
};
} // namespace detail
} // namespace crow

View File

@ -34,23 +34,18 @@ namespace crow
};
template<int N, typename Context, typename Container, typename CurrentMW, typename... Middlewares>
bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx);
template<typename... Middlewares>
struct context : private partial_context<Middlewares...>
//struct context : private Middlewares::context... // simple but less type-safe
{
template<int N, typename Context, typename Container>
template<template<typename QueryMW> class CallCriteria, int N, typename Context, typename Container>
friend typename std::enable_if<(N == 0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res);
template<int N, typename Context, typename Container>
template<template<typename QueryMW> class CallCriteria, int N, typename Context, typename Container>
friend typename std::enable_if<(N > 0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res);
template<int N, typename Context, typename Container, typename CurrentMW, typename... Middlewares2>
friend bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx);
template<template<typename QueryMW> class CallCriteria, int N, typename Context, typename Container>
friend typename std::enable_if<(N < std::tuple_size<typename std::remove_reference<Container>::type>::value), bool>::type
middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx);
template<typename T>
typename T::context& get()

View File

@ -15,6 +15,7 @@
#include "crow/logging.h"
#include "crow/websocket.h"
#include "crow/mustache.h"
#include "crow/middleware.h"
namespace crow
{
@ -44,7 +45,7 @@ namespace crow
return {};
}
virtual void handle(const request&, response&, const routing_params&) = 0;
virtual void handle(request&, response&, const routing_params&) = 0;
virtual void handle_upgrade(const request&, response& res, SocketAdaptor&&)
{
res = response(404);
@ -109,7 +110,7 @@ namespace crow
{
H1& handler;
const routing_params& params;
const request& req;
request& req;
response& res;
};
@ -251,7 +252,7 @@ namespace crow
typename handler_type_helper<ArgsWrapped...>::type handler_;
void operator()(const request& req, response& res, const routing_params& params)
void operator()(request& req, response& res, const routing_params& params)
{
detail::routing_handler_call_helper::call<
detail::routing_handler_call_helper::call_params<
@ -378,7 +379,7 @@ namespace crow
void validate() override
{}
void handle(const request&, response& res, const routing_params&) override
void handle(request&, response& res, const routing_params&) override
{
res = response(404);
res.end();
@ -490,7 +491,7 @@ namespace crow
}
}
void handle(const request& req, response& res, const routing_params& params) override
void handle(request& req, response& res, const routing_params& params) override
{
if (!custom_templates_base.empty())
mustache::set_base(custom_templates_base);
@ -518,7 +519,7 @@ namespace crow
#else
template<typename Func, unsigned... Indices>
#endif
std::function<void(const request&, response&, const routing_params&)>
std::function<void(request&, response&, const routing_params&)>
wrap(Func f, black_magic::seq<Indices...>)
{
#ifdef CROW_MSVC_WORKAROUND
@ -547,7 +548,7 @@ namespace crow
}
private:
std::function<void(const request&, response&, const routing_params&)> erased_handler_;
std::function<void(request&, response&, const routing_params&)> erased_handler_;
};
/// Default rule created when CROW_ROUTE is called.
@ -570,94 +571,18 @@ namespace crow
}
template<typename Func>
typename std::enable_if<black_magic::CallHelper<Func, black_magic::S<Args...>>::value, void>::type
operator()(Func&& f)
{
static_assert(black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
black_magic::CallHelper<Func, black_magic::S<crow::request, Args...>>::value,
"Handler type is mismatched with URL parameters");
static_assert(!std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
"Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable");
void operator()(Func&& f) {
handler_ = (
#ifdef CROW_CAN_USE_CPP14
[f = std::move(f)]
#else
[f]
#endif
(const request&, response& res, Args... args) {
res = response(f(args...));
res.end();
(crow::request& req, crow::response& res, Args... args) {
detail::wrapped_handler_call(req, res, f, std::forward<Args>(args)...);
});
}
template<typename Func>
typename std::enable_if<
!black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
black_magic::CallHelper<Func, black_magic::S<crow::request, Args...>>::value,
void>::type
operator()(Func&& f)
{
static_assert(black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
black_magic::CallHelper<Func, black_magic::S<crow::request, Args...>>::value,
"Handler type is mismatched with URL parameters");
static_assert(!std::is_same<void, decltype(f(std::declval<crow::request>(), std::declval<Args>()...))>::value,
"Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable");
handler_ = (
#ifdef CROW_CAN_USE_CPP14
[f = std::move(f)]
#else
[f]
#endif
(const crow::request& req, crow::response& res, Args... args) {
res = response(f(req, args...));
res.end();
});
}
template<typename Func>
typename std::enable_if<
!black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
!black_magic::CallHelper<Func, black_magic::S<crow::request, Args...>>::value &&
black_magic::CallHelper<Func, black_magic::S<crow::response&, Args...>>::value,
void>::type
operator()(Func&& f)
{
static_assert(black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
black_magic::CallHelper<Func, black_magic::S<crow::response&, Args...>>::value,
"Handler type is mismatched with URL parameters");
static_assert(std::is_same<void, decltype(f(std::declval<crow::response&>(), std::declval<Args>()...))>::value,
"Handler function with response argument should have void return type");
handler_ = (
#ifdef CROW_CAN_USE_CPP14
[f = std::move(f)]
#else
[f]
#endif
(const crow::request&, crow::response& res, Args... args) {
f(res, args...);
});
}
template<typename Func>
typename std::enable_if<
!black_magic::CallHelper<Func, black_magic::S<Args...>>::value &&
!black_magic::CallHelper<Func, black_magic::S<crow::request, Args...>>::value &&
!black_magic::CallHelper<Func, black_magic::S<crow::response&, Args...>>::value,
void>::type
operator()(Func&& f)
{
static_assert(black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
black_magic::CallHelper<Func, black_magic::S<crow::request, Args...>>::value ||
black_magic::CallHelper<Func, black_magic::S<crow::request, crow::response&, Args...>>::value,
"Handler type is mismatched with URL parameters");
static_assert(std::is_same<void, decltype(f(std::declval<crow::request>(), std::declval<crow::response&>(), std::declval<Args>()...))>::value,
"Handler function with response argument should have void return type");
handler_ = std::move(f);
}
template<typename Func>
void operator()(std::string name, Func&& f)
{
@ -665,7 +590,7 @@ namespace crow
(*this).template operator()<Func>(std::forward(f));
}
void handle(const request& req, response& res, const routing_params& params) override
void handle(request& req, response& res, const routing_params& params) override
{
if (!custom_templates_base.empty())
mustache::set_base(custom_templates_base);
@ -673,17 +598,28 @@ namespace crow
mustache::set_base("templates");
detail::routing_handler_call_helper::call<
detail::routing_handler_call_helper::call_params<
decltype(handler_)>,
0, 0, 0, 0,
black_magic::S<Args...>,
black_magic::S<>>()(
detail::routing_handler_call_helper::call_params<
decltype(handler_)>{handler_, params, req, res});
detail::routing_handler_call_helper::call_params<decltype(handler_)>,
0, 0, 0, 0,
black_magic::S<Args...>,
black_magic::S<>>()
(
detail::routing_handler_call_helper::call_params<decltype(handler_)>
{handler_, params, req, res}
);
}
/// Enable local middleware for this handler
template<typename App, typename... Middlewares>
crow::detail::handler_call_bridge<TaggedRule<Args...>, App, Middlewares...>
middlewares()
{
// the handler_call_bridge allows the functor to be placed directly after this function
// instead of wrapping it with more parentheses
return {this};
}
private:
std::function<void(const crow::request&, crow::response&, Args...)> handler_;
std::function<void(crow::request&, crow::response&, Args...)> handler_;
};
const int RULE_SPECIAL_REDIRECT_SLASH = 1;
@ -1507,7 +1443,7 @@ namespace crow
return std::string();
}
void handle(const request& req, response& res)
void handle(request& req, response& res)
{
HTTPMethod method_actual = req.method;
if (req.method >= HTTPMethod::InternalMethodCount)

View File

@ -237,6 +237,37 @@ namespace crow
static constexpr bool value = sizeof(__test<F, Args...>(0)) == sizeof(char);
};
// Check Tuple contains type T
template <typename T, typename Tuple>
struct has_type;
template <typename T>
struct has_type<T, std::tuple<>> : std::false_type {};
template <typename T, typename U, typename... Ts>
struct has_type<T, std::tuple<U, Ts...>> : has_type<T, std::tuple<Ts...>> {};
template <typename T, typename... Ts>
struct has_type<T, std::tuple<T, Ts...>> : std::true_type {};
// Check F is callable with Args
template<typename F, typename...Args>
struct is_callable
{
template<typename F2, typename...Args2>
static std::true_type __test(decltype(std::declval<F2>()(std::declval<Args2>()...)) *) { return {}; }
template<typename F2, typename...Args2>
static std::false_type __test(...) { return {}; }
static constexpr bool value = decltype(__test<F, Args...>(nullptr))::value;
};
// Kind of fold expressions in C++11
template<bool...>
struct bool_pack;
template<bool... bs>
using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>;
template<int N>
struct single_tag_to_type

View File

@ -44,10 +44,11 @@ TEST_CASE("Rule")
r.validate();
response res;
request req;
// executing handler
CHECK(0 == x);
r.handle(request(), res, routing_params());
r.handle(req, res, routing_params());
CHECK(1 == x);
// registering handler with request argument
@ -60,7 +61,7 @@ TEST_CASE("Rule")
// executing handler
CHECK(1 == x);
r.handle(request(), res, routing_params());
r.handle(req, res, routing_params());
CHECK(2 == x);
} // Rule
@ -1374,6 +1375,56 @@ TEST_CASE("middleware_context")
app.stop();
} // middleware_context
struct LocalSecretMiddleware : crow::ILocalMiddleware {
struct context
{};
void before_handle(request& /*req*/, response& res, context& /*ctx*/)
{
res.code = 403;
res.end();
}
void after_handle(request& /*req*/, response& /*res*/, context& /*ctx*/)
{}
};
TEST_CASE("local_middleware")
{
App<LocalSecretMiddleware> app;
CROW_ROUTE(app, "/")
([]() {
return "works!";
});
CROW_ROUTE(app, "/secret")
.middlewares<decltype(app), LocalSecretMiddleware>()
([]() {
return "works!";
});
app.validate();
// Local middleware is handled at router level, so we don't have to send requests manually
{
request req;
response res;
req.url = "/";
app.handle(req, res);
CHECK(200 == res.code);
}
{
request req;
response res;
req.url = "/secret";
app.handle(req, res);
CHECK(403 == res.code);
}
} // local_middleware
TEST_CASE("middleware_cookieparser")
{
static char buf[2048];