Skip to content

Commit a36d5c3

Browse files
authored
Merge pull request ipkn#403 from dranikpg/blueprint-middleware
Blueprint middleware
2 parents c897101 + a6bf90f commit a36d5c3

File tree

10 files changed

+412
-167
lines changed

10 files changed

+412
-167
lines changed

docs/guides/middleware.md

Lines changed: 48 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,78 @@
1-
Middleware is used for altering and inspecting requests before and after the handler call.
1+
Middleware is used for altering and inspecting requests before and after calling the handler. In Crow it's very similar to middleware in other web frameworks.
2+
3+
All middleware is registered in the Crow application
4+
5+
```cpp
6+
crow::App<FirstMW, SecondMW, ThirdMW> app;
7+
```
8+
9+
and is called in this specified order.
210

311
Any middleware requires the following 3 members:
412

5-
* A context struct for storing the middleware data.
6-
* A `before_handle` method, which is called before the handler. If `res.end()` is called, the operation is halted.
13+
* A context struct for storing request local data.
14+
* A `before_handle` method, which is called before the handler.
715
* A `after_handle` method, which is called after the handler.
816

9-
## before_handle
10-
There are two possible signatures for before_handle
17+
!!! warning
1118

12-
1. if you only need to access this middleware's context.
19+
As soon as `response.end()` is called, no other handlers and middleware is run, except for after_handlers of already visited middleware.
1320

14-
```cpp
15-
void before_handle(request& req, response& res, context& ctx)
16-
```
1721

18-
2. To get access to other middlewares context
22+
## Example
1923

20-
``` cpp
21-
template <typename AllContext>
22-
void before_handle(request& req, response& res, context& ctx, AllContext& all_ctx)
24+
A middleware that can be used to guard admin handlers
25+
26+
```cpp
27+
struct AdminAreaGuard
28+
{
29+
struct context
30+
{};
31+
32+
void before_handle(crow::request& req, crow::response& res, context& ctx)
2333
{
24-
auto other_ctx = all_ctx.template get<OtherMiddleware>();
34+
if (req.remote_ip_address != ADMIN_IP)
35+
{
36+
res.code = 403;
37+
res.end();
38+
}
2539
}
26-
```
40+
41+
void after_handle(crow::request& req, crow::response& res, context& ctx)
42+
{}
43+
};
44+
```
2745

2846

29-
## after_handle
30-
There are two possible signatures for after_handle
47+
### before_handle and after_handle
48+
There are two possible signatures for before_handle and after_handle
3149

3250
1. if you only need to access this middleware's context.
3351

3452
```cpp
35-
void after_handle(request& req, response& res, context& ctx)
53+
void before_handle(request& req, response& res, context& ctx)
3654
```
3755

3856
2. To get access to other middlewares context
3957

4058
``` cpp
4159
template <typename AllContext>
42-
void after_handle(request& req, response& res, context& ctx, AllContext& all_ctx)
60+
void before_handle(request& req, response& res, context& ctx, AllContext& all_ctx)
4361
{
4462
auto other_ctx = all_ctx.template get<OtherMiddleware>();
4563
}
4664
```
4765

48-
## Using middleware
66+
## Local middleware
4967

50-
All middleware has to be registered in the Crow application and is enabled globally by default.
51-
52-
```cpp
53-
crow::App<FirstMiddleware, SecondMiddleware> app;
54-
```
55-
56-
if you want to enable some middleware only for specific handlers, you have to extend it from `crow::ILocalMiddleware`.
68+
By default, every middleware is called for each request. If you want to enable middleware for specific handlers or blueprints, you have to extend it from `crow::ILocalMiddleware`
5769

5870
```cpp
5971
struct LocalMiddleware : crow::ILocalMiddleware
6072
{
61-
...
6273
```
6374
64-
After this, you can enable it for specific handlers.
75+
After this, you can enable it for specific handlers
6576
6677
```cpp
6778
CROW_ROUTE(app, "/with_middleware")
@@ -71,26 +82,14 @@ CROW_ROUTE(app, "/with_middleware")
7182
});
7283
```
7384

74-
## Examples
75-
76-
A local middleware that can be used to guard admin handlers
85+
or blueprints
7786

7887
```cpp
79-
struct AdminAreaGuard : crow::ILocalMiddleware
80-
{
81-
struct context
82-
{};
83-
84-
void before_handle(crow::request& req, crow::response& res, context& ctx)
85-
{
86-
if (req.remote_ip_address != ADMIN_IP)
87-
{
88-
res.code = 403;
89-
res.end();
90-
}
91-
}
92-
93-
void after_handle(crow::request& req, crow::response& res, context& ctx)
94-
{}
95-
};
88+
Blueprint bp("with_middleware");
89+
bp.CROW_MIDDLEWARES(app, FistLocalMiddleware, SecondLocalMiddleware);
9690
```
91+
92+
!!! warning
93+
94+
Local and global middleware are called separately. First all global middleware is run, then all enabled local middleware for the current handler is run. In both cases middleware is called strongly
95+
in the order listed in the Crow application.

examples/example_middleware.cpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ struct RequestLogger
55
struct context
66
{};
77

8+
// This method is run before handling the request
89
void before_handle(crow::request& req, crow::response& /*res*/, context& /*ctx*/)
910
{
1011
CROW_LOG_INFO << "Request to:" + req.url;
1112
}
1213

14+
// This method is run after handling the request
1315
void after_handle(crow::request& /*req*/, crow::response& /*res*/, context& /*ctx*/)
1416
{}
1517
};
@@ -23,6 +25,7 @@ struct SecretContentGuard : crow::ILocalMiddleware
2325

2426
void before_handle(crow::request& /*req*/, crow::response& res, context& /*ctx*/)
2527
{
28+
// A request can be aborted prematurely
2629
res.write("SECRET!");
2730
res.code = 403;
2831
res.end();
@@ -32,10 +35,28 @@ struct SecretContentGuard : crow::ILocalMiddleware
3235
{}
3336
};
3437

38+
struct RequestAppend : crow::ILocalMiddleware
39+
{
40+
// Values from this context can be accessed from handlers
41+
struct context
42+
{
43+
std::string message;
44+
};
45+
46+
void before_handle(crow::request& /*req*/, crow::response& /*res*/, context& /*ctx*/)
47+
{}
48+
49+
void after_handle(crow::request& /*req*/, crow::response& res, context& ctx)
50+
{
51+
// The response can be modified
52+
res.write(" + (" + ctx.message + ")");
53+
}
54+
};
55+
3556
int main()
3657
{
3758
// ALL middleware (including per handler) is listed
38-
crow::App<RequestLogger, SecretContentGuard> app;
59+
crow::App<RequestLogger, SecretContentGuard, RequestAppend> app;
3960

4061
CROW_ROUTE(app, "/")
4162
([]() {
@@ -48,7 +69,20 @@ int main()
4869
return "";
4970
});
5071

51-
app.port(18080).run();
72+
crow::Blueprint bp("bp", "c", "c");
73+
// Register middleware on all routes on a specific blueprint
74+
// This also applies to sub blueprints
75+
bp.CROW_MIDDLEWARES(app, RequestAppend);
5276

77+
CROW_BP_ROUTE(bp, "/")
78+
([&](const crow::request& req) {
79+
// Get RequestAppends context
80+
auto& ctx = app.get_context<RequestAppend>(req);
81+
ctx.message = "World";
82+
return "Hello:";
83+
});
84+
app.register_blueprint(bp);
85+
86+
app.port(18080).run();
5387
return 0;
5488
}

include/crow/app.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ namespace crow
7373
/// Process the request and generate a response for it
7474
void handle(request& req, response& res)
7575
{
76-
router_.handle(req, res);
76+
router_.handle<self_t>(req, res);
7777
}
7878

7979
/// Create a dynamic route using a rule (**Use CROW_ROUTE instead**)

include/crow/http_connection.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ namespace crow
161161
req.io_service = &adaptor_.get_io_service();
162162

163163
detail::middleware_call_helper<detail::middleware_call_criteria_only_global,
164-
0, decltype(ctx_), decltype(*middlewares_)>(*middlewares_, req, res, ctx_);
164+
0, decltype(ctx_), decltype(*middlewares_)>({}, *middlewares_, req, res, ctx_);
165165

166166
if (!res.completed_)
167167
{
@@ -198,7 +198,7 @@ namespace crow
198198
detail::middleware_call_criteria_only_global,
199199
(static_cast<int>(sizeof...(Middlewares)) - 1),
200200
decltype(ctx_),
201-
decltype(*middlewares_)>(*middlewares_, ctx_, req_, res);
201+
decltype(*middlewares_)>({}, *middlewares_, ctx_, req_, res);
202202
}
203203
#ifdef CROW_ENABLE_COMPRESSION
204204
if (handler_->compression_used())

include/crow/http_response.h

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,15 @@ namespace crow
1919
template<typename Adaptor, typename Handler, typename... Middlewares>
2020
class Connection;
2121

22-
namespace detail
23-
{
24-
template<typename F, typename App, typename... Middlewares>
25-
struct handler_middleware_wrapper;
26-
} // namespace detail
22+
class Router;
2723

2824
/// HTTP response
2925
struct response
3026
{
3127
template<typename Adaptor, typename Handler, typename... Middlewares>
3228
friend class crow::Connection;
3329

34-
template<typename F, typename App, typename... Middlewares>
35-
friend struct crow::detail::handler_middleware_wrapper;
30+
friend class Router;
3631

3732
int code{200}; ///< The Status code for the response.
3833
std::string body; ///< The actual payload containing the response data.

0 commit comments

Comments
 (0)