* `res.send()` Send a response of various types.
* `res.error()` Set the response status code and send its message as the response body.
+## Response cycle
+
+When the popcorn `App` receives a request, the response cycle is the following:
+
+1. `pre-middlewares` lookup matching middlewares registered with `use_before(pre_middleware)`:
+ 1. execute matching middleware by registration order
+ 2. if a middleware send a response then let the `pre-middlewares` loop continue
+ with the next middleware
+2. `response-handlers` lookup matching handlers registered with `use(handler)`:
+ 1. execute matching middleware by registration order
+ 2. if a middleware send a response then stop the `response-handlers` loop
+ 3. if no hander matches or sends a response, generate a 404 response
+3. `post-middlewares` lookup matching handlers registered with `use_after(post_handler)`:
+ 1. execute matching middleware by registration order
+ 2. if a middleware send a response then let the `post-middlewares` loop continue
+ with the next middleware
+
## Middlewares
### Overview
var app = new App
-app.use("/*", new MyLogger)
+app.use_before("/*", new MyLogger)
app.use("/", new HelloHandler)
app.listen("localhost", 3000)
~~~
(even 404 ones) pass through the middleware handler.
This handler just prints “Request Logged!” when a request is received.
-The order of middleware loading is important: middleware functions that are loaded first are also executed first.
-In the above example, `MyLogger` will be executed before `HelloHandler`.
+Be default, the order of middleware execution is that are loaded first are also executed first.
+To ensure our middleware `MyLogger` will be executed before all the other, we add it
+with the `use_before` method.
### Ultra cool, more advanced logger example
end
var app = new App
-app.use("/*", new RequestTimeHandler)
+app.use_before("/*", new RequestTimeHandler)
app.use("/", new HelloHandler)
-app.use("/*", new LogHandler)
+app.use_after("/*", new LogHandler)
app.listen("localhost", 3000)
~~~
from the `req` parameter.
We use the new middleware called `RequestTimeHandler` to initialize the request timer.
+Because of the `use_before` method, the `RequestTimeHandler` middleware will be executed
+before all the others.
+
+We then let the `HelloHandler` produce the response.
Finally, our `LogHandler` will display a bunch of data and use the request `timer`
to display the time it took to process the request.
+Because of the `use_after` method, the `LogHandler` middleware will be executed after
+all the others.
The app now uses the `RequestTimeHandler` middleware for every requests received
by the Popcorn app.
end
var app = new App
-app.use("/*", new RequestTimeHandler)
+app.use_before("/*", new RequestTimeHandler)
app.use("/", new HelloHandler)
-app.use("/*", new LogHandler)
+app.use_after("/*", new LogHandler)
app.listen("localhost", 3000)
var app = new App
-app.use("/*", new LogHandler)
+app.use_before("/*", new LogHandler)
app.use("/", new HelloHandler)
app.listen("localhost", 3000)
# List of handlers to match with requests.
private var handlers = new Map[AppRoute, Handler]
+ # List of handlers to match before every other.
+ private var pre_handlers = new Map[AppRoute, Handler]
+
+ # List of handlers to match after every other.
+ private var post_handlers = new Map[AppRoute, Handler]
+
# Register a `handler` for a route `path`.
#
# Route paths are matched in registration order.
fun use(path: String, handler: Handler) do
- var route
- if handler isa Router or handler isa StaticHandler then
- route = new AppGlobRoute(path)
- else if path.has_suffix("*") then
- route = new AppGlobRoute(path)
- else
- route = new AppParamRoute(path)
- end
+ var route = build_route(handler, path)
handlers[route] = handler
end
+ # Register a pre-handler for a route `path`.
+ #
+ # Prehandlers are matched before every other handlers in registrastion order.
+ fun use_before(path: String, handler: Handler) do
+ var route = build_route(handler, path)
+ pre_handlers[route] = handler
+ end
+
+ # Register a post-handler for a route `path`.
+ #
+ # Posthandlers are matched after every other handlers in registrastion order.
+ fun use_after(path: String, handler: Handler) do
+ var route = build_route(handler, path)
+ post_handlers[route] = handler
+ end
+
redef fun handle(route, uri, req, res) do
if not route.match(uri) then return
+ handle_pre(route, uri, req, res)
+ handle_in(route, uri, req, res)
+ handle_post(route, uri, req, res)
+ end
+
+ private fun handle_pre(route: AppRoute, uri: String, req: HttpRequest, res: HttpResponse) do
+ for hroute, handler in pre_handlers do
+ handler.handle(hroute, route.uri_root(uri), req, res)
+ end
+ end
+
+ private fun handle_in(route: AppRoute, uri: String, req: HttpRequest, res: HttpResponse) do
for hroute, handler in handlers do
handler.handle(hroute, route.uri_root(uri), req, res)
if res.sent then break
end
end
+
+ private fun handle_post(route: AppRoute, uri: String, req: HttpRequest, res: HttpResponse) do
+ for hroute, handler in post_handlers do
+ handler.handle(hroute, route.uri_root(uri), req, res)
+ end
+ end
+
+ private fun build_route(handler: Handler, path: String): AppRoute do
+ if handler isa Router or handler isa StaticHandler then
+ return new AppGlobRoute(path)
+ else if path.has_suffix("*") then
+ return new AppGlobRoute(path)
+ else
+ return new AppParamRoute(path)
+ end
+ end
end
# Popcorn application.
redef fun answer(req, uri) do
uri = uri.simplify_path
var res = new HttpResponse(404)
+ for route, handler in pre_handlers do
+ handler.handle(route, uri, req, res)
+ end
for route, handler in handlers do
handler.handle(route, uri, req, res)
+ if res.sent then break
end
if not res.sent then
res.send(error_tpl(res.status_code, res.status_message), 404)
end
+ for route, handler in post_handlers do
+ handler.handle(route, uri, req, res)
+ end
res.session = req.session
return res
end
</html>
[Client] curl -s localhost:*****/
-Warning: Headers already sent!
-<!DOCTYPE html>
-<html>
- <body>
- <h1>Another Index</h1>
- </body>
-</html>
<!DOCTYPE html>
<html>
<body>
end
var app = new App
-app.use("/*", new RequestTimeHandler)
+app.use_before("/*", new RequestTimeHandler)
app.use("/", new HelloHandler)
-app.use("/*", new LogHandler)
+app.use_after("/*", new LogHandler)
var host = test_host
var port = test_port
end
var app = new App
-app.use("/*", new LogHandler)
+app.use_before("/*", new LogHandler)
app.use("/", new HelloHandler)
var host = test_host